在本篇隨筆裡將會分析一下hibernate的緩存機制,包括一級緩存(session級別)、二級緩存(sessionFactory級別)以及查詢緩存,當然還要討論下我們的N+1的問題。
隨筆雖長,但我相信看完的朋友絕對能對hibernate的 N+1問題以及緩存有更深的了解。
一、N+1問題
首先我們來探討一下N+1的問題,我們先通過一個例子來看一下,什麼是N+1問題:
list()獲得對象:
/** * 此時會發出一條sql,將30個學生全部查詢出來 */ List<Student> ls = (List<Student>)session.createQuery("from Student") .setFirstResult(0).setMaxResults(30).list(); Iterator<Student> stus =ls.iterator(); for(;stus.hasNext();) { Student stu = (Student)stus.next(); System.out.println(stu.getName()); }
如果通過list()方法來獲得對象,毫無疑問,hibernate會發出一條sql語句,將所有的對象查詢出來,這點相信大家都能理解
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student0_.sex as sex2_ from t_student student0_ limit ?
那麼,我們再來看看iterator()這種情況
iterator()獲得對象
/** * 如果使用iterator方法返回列表,對於hibernate而言,它僅僅只是發出取id列表的sql * 在查詢相應的具體的某個學生信息時,會發出相應的SQL去取學生信息 * 這就是典型的N+1問題 * 存在iterator的原因是,有可能會在一個session中查詢兩次數據,如果使用list每一次都會把所有的對象查詢上來 * 而是要iterator僅僅只會查詢id,此時所有的對象已經存儲在一級緩存(session的緩存)中,可以直接獲取 */ Iterator<Student> stus = (Iterator<Student>)session.createQuery("from Student") .setFirstResult(0).setMaxResults(30).iterate(); for(;stus.hasNext();) { Student stu = (Student)stus.next(); System.out.println(stu.getName()); }
在執行完上述的測試用例後,我們來看看控制台的輸出,看會發出多少條 sql 語句:
Hibernate: select student0_.id as col_0_0_ from t_student student0_ limit ?Hibernate: select student0_.id as id2_0_, student0_.name as name2_0_, student0_.rid as rid2_0_, student0_.sex as sex2_0_ from t_student student0_ where student0_.id=?