通過一道面試題來引入Hibernate之懶載入
前言
先來看看面試題:
Hibernate中get和load方法的區別?
答:相同點:都是通過主鍵查詢的方法。session.get(User.class,1);session.load(User.class,1);
不同點:
get: 及時載入,只要呼叫get方法立刻向資料庫查詢,,執行sql語句。
load:預設使用懶載入,當用到資料的時候才向資料庫查詢。 相當於做了一層優化。
懶載入:(lazy)
概念:當用到資料的時候才向資料庫查詢,這就是hibernate的懶載入特性。
目的:提供程式執行效率!
在配置檔案中設定lazy 值:
true 預設情況下使用懶載入
false 關閉懶載入
extra (在集合資料懶載入時候提升效率)
在真正使用資料的時候才向資料庫傳送查詢的sql;
如果呼叫集合的size()/isEmpty()方法,只是統計,不真正查詢資料!
集合屬性預設有懶載入!
案例分析:
本本案列均來自Hibernate系列篇中講解了部門與員工的案例,這裡直接拿程式碼來測試了。如有不明白的地方,請參考作者之前的Hibernate筆記。
先測試get方法—Lazy使用預設值true.
@Test
public void get_load() {
Session session = sf.openSession();
session.beginTransaction ();
Dept dept = new Dept();
// get: 及時查詢
dept = (Dept) session.get(Dept.class, 9);
System.out.println(“---------------------------”);
System.out.println(dept.getDeptName());
session.getTransaction().commit();
session.close();
}
列印結果:先列印sql語句,再列印“————”,最後列印部門名稱。說明get方法是直接執行資料庫查詢的(不猶豫,很果斷)。
測試load方法
@Test
public void get_load() {
Session session = sf.openSession();
session.beginTransaction();
Dept dept = new Dept();
// get: 及時查詢
// dept = (Dept) session.get(Dept.class, 9);
// System.out.println(dept.getDeptName());
// load,預設懶載入, 及在使用資料的時候,才向資料庫傳送查詢的sql語句!
dept = (Dept)session.load(Dept.class, 9);
System.out.println(“---------------------------”);
// 在這裡使用
System.out.println(dept.getDeptName());
session.getTransaction().commit();
session.close();
}
列印結果;先列印“—————–”,再列印sql查詢語句,最後再列印部門的名稱。說明load方法使用了懶載入機制,在用到dept資料的時候才向資料庫傳送查詢的sql語句!
注意:在session關閉之後,不能使用懶載入資料,也就是load查詢到的資料比如user物件,如果在session.close()之後,進行user.getName()將會報錯。
比如:程式碼如下:
@Test
public void get_load() {
Session session = sf.openSession();
session.beginTransaction();
Dept dept = new Dept();
// get: 及時查詢
// dept = (Dept) session.get(Dept.class, 9);
// System.out.println(dept.getDeptName());
// load,預設懶載入, 及在使用資料的時候,才向資料庫傳送查詢的sql語句!
dept = (Dept)session.load(Dept.class, 9);
System.out.println(“---------------------------”);
session.getTransaction().commit();
session.close();
// 在這裡使用
System.out.println(dept.getDeptName());
}
會報錯!錯誤資訊:org.hibernate.LazyInitializationException: could not initialize proxy - no Session
還有,在使用load方法時將會返回查詢結果的代理物件。
例如:user=(User)session.load(User.class,1); 此時的user型別為User物件的代理物件。我們可以通過debug斷點除錯的方法檢視dept的型別發生了變化。
懶載入在集合中的應用:
@Test
public void set() {
Session session = sf.openSession();
session.beginTransaction();
Dept dept = (Dept) session.get(Dept.class, 10);
System.out.println(dept.getDeptName());
System.out.println("------");
System.out.println(dept.getEmps()); // SQL
session.getTransaction().commit();
session.close();
}
列印順序:先查詢部門的sql語句,然後列印部門的名稱,再列印“———–”,然後查詢員工的sql語句,最後列印該部門下的全部的員工。這就是懶載入在集合中的應用,即如果在具體的實際開發中,我們只想要部門的名稱等資訊,不會去關心該部門下的具體員工,那麼將lazy=true,會提高Hibernate對資料庫操作的效率。
但是有些時候,我們又想查詢部門的時候,也同時查出來員工的資訊,那麼就必須把lazy改為flase.因為有時在實際的專案中查詢跟資料庫操作有關,一般會放在service層中,如果在頁面載入之前呼叫一次service層中的方法,就想查詢部門和員工的資訊,並且在頁面中顯示,那麼怎麼辦呢?這就考慮關閉懶載入了。之後的專案開發案例中會給出具體的應用。
那麼還有一點,如何解決session關閉後不能使用懶載入資料的問題?
// 方式1: 在session關閉之前先使用一下資料
//dept.getDeptName();
// 方式2:session關閉之前強迫代理物件初始化
Hibernate.initialize(dept);
// 方式3:關閉懶載入
設定lazy=false;
// 方式4: 在使用資料之後,再關閉session!
測試程式碼如下:
@Test
public void get_load() {
Session session = sf.openSession();
session.beginTransaction();
Dept dept = new Dept();
// get: 及時查詢
// dept = (Dept) session.get(Dept.class, 9);
// System.out.println(dept.getDeptName());
// load,預設懶載入, 及在使用資料的時候,才向資料庫傳送查詢的sql語句!
dept = (Dept)session.load(Dept.class, 9);
// 方式1: 先使用一下資料
//dept.getDeptName();
// 方式2:強迫代理物件初始化
Hibernate.initialize(dept);
// 方式3:關閉懶載入
session.getTransaction().commit();
session.close();
// 在這裡使用
System.out.println(dept.getDeptName());
}
最後,說一下集合中lazy=extra 的用法。
如果呼叫集合的size()/isEmpty()方法,只是統計,不真正查詢資料!
測試:
@Test
public void set() {
Session session = sf.openSession();
session.beginTransaction();
Dept dept = (Dept) session.get(Dept.class, 10);
System.out.println(dept.getDeptName());
System.out.println("------");
System.out.println(dept.getEmps().isEmpty()); // SQL
session.getTransaction().commit();
session.close();
}
輸出順序:部門名稱,“—-”然後是“select count(id) form t_employee‘’的sql語句,說明size或者isEmpty方法只會查詢員工表的總記錄數,不會查詢員工的全部資訊。這也算是一種優化策略!