Hibernate中Session之get和load方法的真正區別

 最近在學習SHH框架中的Hibernate,對Session的get和load方法,有點混不清楚,不知道區別在哪,或者對它們的區別感觸不深。所以百度了一下,結果問題來了。百度的結果和實際測試的結果出入很大。主要是對get方法的說法跟實際運行的結果不一致。


      先說一下觀點吧:

  • get不支持lazy,load支持lazy;
  • 數據不存在時,get返回null,load則拋出ObjectNotFoundException異常。
  • load方法可以返回實體的代理類實例,而get方法直接讀取數據庫,所以直接返回實體類(get的這個說法是錯誤的

      對於第一條,相信大家都沒有太多的疑問。我這裏給個例子稍作解釋:lazy意味着用的時候纔去執行sql語句。
[java] view plaincopy
  1. User user = (User)session.load(User.class,"4028981b41174a690141174a6c6d0003");     
這句代碼不會去執行數據庫查詢,只有用到user時纔會去執行數據庫查詢。所以不會立即生成sql語句。
[java] view plaincopy
  1. User user = (User)session.get(User.class"4028981b41174a690141174a6c6d0003");      
而上面這句代碼則會立即去執行數據庫查詢(如果緩存中沒有實例)。

      而後面的問題要想說明白,首先得了解一個問題——Session加載實體對象的過程:


      首先,Hibernate中維持了兩級緩存。第一級緩存由Session實例維護,它是屬於事務範圍的緩存。其中保持了Session當前所有關聯實體的數據,也稱爲內部緩存。而第二級緩存則存在於SessionFactory層次,它是屬於進程範圍或羣集範圍的緩存,由當前所有由本SessionFactory構造的Session實例共享。


      出於性能考慮,避免無謂的數據庫訪問,Session在調用數據庫查詢功能之前,會先在緩存中進行查詢。首先在第一級緩存(內部緩存)中,通過實體類型和id進行查找,如果第一級緩存查找命中,且數據狀態合法,則直接返回。然後,Session會在當前“NonExists”記錄中進行查找,如果“NonExists”記錄中存在同樣的查詢條件,則返回null。 “NonExists” 記錄了當前Session實例在之前所有查詢操作中,未能查詢到有效數據的查詢條件(相當於一個查詢黑名單列表)。如此一來,如果Session中一個無效的查詢條件重複出現,即可迅速作出判斷,從而獲得最佳的性能表現。


      對於load方法而言,首先查找內部緩存,如果命中,則返回實例,如果內部緩存中未發現有效數據,則查詢第二級緩存,如果第二級緩存命中,則返回。若二級緩存中依舊未發現有效數據,則發起數據庫查詢操作(Select SQL)。如果查詢到,則返回實體類的代理對象,若經過查詢未發現對應記錄,則將此次查詢的信息在“NonExists” 中加以記錄,並拋出ObjectNotFoundException異常。

      對於get方法而言,許多書上、網絡博客裏都說錯了。get方法同樣是先查找內部緩存,如果命中,則返回,否則發起數據庫查詢操作,如果查詢到,則返回實體類的對象,若經過查詢未發現對應記錄,則將此次查詢的信息在“NonExists” 中加以記錄,並返回null。所以網絡上說的“當他人修改了數據後,用load可能讀取不到最新的數據,而get肯定可以讀取到最新修改的數據”的說法也是不成立的。

      這也就意味着,get方法獲取到的並不一定是實體類對象,load方法也不一定是返回實體代理類對象。

      以上的觀點都是我通過測試得出來的,有代碼有圖有真相呀:
[java] view plaincopy
  1. package com.bjpowernode.hibernate;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import junit.framework.TestCase;  
  6.   
  7. import org.hibernate.ObjectNotFoundException;  
  8. import org.hibernate.Session;  
  9. import org.hibernate.Transaction;  
  10.   
  11. /** 
  12.  * Session測試類 
  13.  *  
  14.  * @author Longxuan 
  15.  *  
  16.  */  
  17. public class SessionTest extends TestCase {  
  18.   
  19.     public void testEquals() {  
  20.         Session session = null;  
  21.         try {  
  22.               
  23.             //獲取Session  
  24.             session = HibernateUtils.getSession();  
  25.             // 開啓事務  
  26.             session.beginTransaction();  
  27.             System.out.println("\n\n\n\n");  
  28.             try {  
  29.                   
  30.                 // 驗證查不到數據時,get返回null,load拋ObjectNotFoundException異常  
  31.                 System.out.println(session.get(User.class"123"));  
  32.                 System.out.println(session.load(User.class"123"));  
  33.                   
  34.             } catch (ObjectNotFoundException e) {  
  35.                   
  36.                 System.out.println("load方法拋出ObjectNotFoundException異常");  
  37.                   
  38.             }  
  39.               
  40.             System.out.println("\n\n");  
  41.               
  42.               
  43.   
  44.             // 驗證load返回實體類對象,而非代理對象  
  45.             {  
  46.                 User user1 = (User) session.get(User.class,"4028981b41174a690141174a6c6d0003");  
  47.                 User user2 = (User) session.load(User.class,"4028981b41174a690141174a6c6d0003");  
  48.                 System.out.println("user1:" + user1.getClass().getSimpleName());  
  49.                 System.out.println("user2:" + user2.getClass().getSimpleName());  
  50.                 System.out.println("user1與 user2是否爲同一對象:" + user1.equals(user2));  
  51.             }  
  52.   
  53.             System.out.println("\n\n");  
  54.             session.clear();//清除Session  
  55.               
  56.               
  57.             // 驗證get也可以返回代理類對象,而並不一定返回實體類對象  
  58.             // 同時驗證了get方法先查找緩存(如果沒有輸出sql語句,則說明get查找了緩存)  
  59.             {  
  60.                 User user3 = (User) session.load(User.class,"4028981b41174a690141174a6c6d0003");  
  61.                 User user4 = (User) session.get(User.class,"4028981b41174a690141174a6c6d0003");  
  62.                 System.out.println("user3:" + user3.getClass().getSimpleName());  
  63.                 System.out.println("user4:" + user4.getClass().getSimpleName());  
  64.                 System.out.println("user3與 user4是否爲同一對象:" + user3.equals(user4));  
  65.             }  
  66.               
  67.                           
  68.             session.getTransaction().commit();  
  69.   
  70.         } catch (Exception e) {  
  71.   
  72.             e.printStackTrace();  
  73.             session.getTransaction().rollback();  
  74.   
  75.         } finally {  
  76.             HibernateUtils.closeSession(session);  
  77.         }  
  78.   
  79.     }  
  80. }  

運行結果圖:



      還有一個有趣的現象:
[java] view plaincopy
  1. User user5 = (User)session.load(User.class"123");       
  2. System.out.println(user5.getId());  
      運行結果直接輸出 123
      從結果中也可以看出,前2句代碼,不會去執行數據庫操作。因爲load後會在hibernate的一級緩存裏存放一個map對象,該map的key就是Id的值,但是當你getId()時,它會去一級緩存裏拿map的key值,正好找到了,所以不會再去執行數據庫查詢。也不會報任何錯。就有了以上的結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章