一口一口吃掉Hibernate(二)——别被世俗蒙蔽了双眼:Hibernate中Session之get和load方法的真正区别
? ? ? 最近在學(xué)習(xí)SHH框架中的Hibernate,對Session的get和load方法,有點(diǎn)混不清楚,不知道區(qū)別在哪,或者對它們的區(qū)別感觸不深。所以百度了一下,結(jié)果問題來了。百度的結(jié)果和實(shí)際測試的結(jié)果出入很大。主要是對get方法的說法跟實(shí)際運(yùn)行的結(jié)果不一致。
? ? ? 先說一下觀點(diǎn)吧:
- get不支持lazy,load支持lazy;
- 數(shù)據(jù)不存在時(shí),get返回null,load則拋出ObjectNotFoundException異常。
- load方法可以返回實(shí)體的代理類實(shí)例,而get方法直接讀取數(shù)據(jù)庫,所以直接返回實(shí)體類(get的這個(gè)說法是錯(cuò)誤的)
? ? ? 對于第一條,相信大家都沒有太多的疑問。我這里給個(gè)例子稍作解釋:lazy意味著用的時(shí)候才去執(zhí)行sql語句。 User user = (User)session.load(User.class,"4028981b41174a690141174a6c6d0003"); 這句代碼不會去執(zhí)行數(shù)據(jù)庫查詢,只有用到user時(shí)才會去執(zhí)行數(shù)據(jù)庫查詢。所以不會立即生成sql語句。
User user = (User)session.get(User.class, "4028981b41174a690141174a6c6d0003"); 而上面這句代碼則會立即去執(zhí)行數(shù)據(jù)庫查詢(如果緩存中沒有實(shí)例)。
? ? ? 而后面的問題要想說明白,首先得了解一個(gè)問題——Session加載實(shí)體對象的過程:
? ? ? 首先,Hibernate中維持了兩級緩存。第一級緩存由Session實(shí)例維護(hù),它是屬于事務(wù)范圍的緩存。其中保持了Session當(dāng)前所有關(guān)聯(lián)實(shí)體的數(shù)據(jù),也稱為內(nèi)部緩存。而第二級緩存則存在于SessionFactory層次,它是屬于進(jìn)程范圍或群集范圍的緩存,由當(dāng)前所有由本SessionFactory構(gòu)造的Session實(shí)例共享。
? ? ? 出于性能考慮,避免無謂的數(shù)據(jù)庫訪問,Session在調(diào)用數(shù)據(jù)庫查詢功能之前,會先在緩存中進(jìn)行查詢。首先在第一級緩存(內(nèi)部緩存)中,通過實(shí)體類型和id進(jìn)行查找,如果第一級緩存查找命中,且數(shù)據(jù)狀態(tài)合法,則直接返回。然后,Session會在當(dāng)前“NonExists”記錄中進(jìn)行查找,如果“NonExists”記錄中存在同樣的查詢條件,則返回null。 “NonExists” 記錄了當(dāng)前Session實(shí)例在之前所有查詢操作中,未能查詢到有效數(shù)據(jù)的查詢條件(相當(dāng)于一個(gè)查詢黑名單列表)。如此一來,如果Session中一個(gè)無效的查詢條件重復(fù)出現(xiàn),即可迅速作出判斷,從而獲得最佳的性能表現(xiàn)。
? ? ? 對于load方法而言,首先查找內(nèi)部緩存,如果命中,則返回實(shí)例,如果內(nèi)部緩存中未發(fā)現(xiàn)有效數(shù)據(jù),則查詢第二級緩存,如果第二級緩存命中,則返回。若二級緩存中依舊未發(fā)現(xiàn)有效數(shù)據(jù),則發(fā)起數(shù)據(jù)庫查詢操作(Select SQL)。如果查詢到,則返回實(shí)體類的代理對象,若經(jīng)過查詢未發(fā)現(xiàn)對應(yīng)記錄,則將此次查詢的信息在“NonExists” 中加以記錄,并拋出ObjectNotFoundException異常。
? ? ? 對于get方法而言,許多書上、網(wǎng)絡(luò)博客里都說錯(cuò)了。get方法同樣是先查找內(nèi)部緩存,如果命中,則返回,否則發(fā)起數(shù)據(jù)庫查詢操作,如果查詢到,則返回實(shí)體類的對象,若經(jīng)過查詢未發(fā)現(xiàn)對應(yīng)記錄,則將此次查詢的信息在“NonExists” 中加以記錄,并返回null。所以網(wǎng)絡(luò)上說的“當(dāng)他人修改了數(shù)據(jù)后,用load可能讀取不到最新的數(shù)據(jù),而get肯定可以讀取到最新修改的數(shù)據(jù)”的說法也是不成立的。
? ? ? 這也就意味著,get方法獲取到的并不一定是實(shí)體類對象,load方法也不一定是返回實(shí)體代理類對象。
? ? ? 以上的觀點(diǎn)都是我通過測試得出來的,有代碼有圖有真相呀: package com.bjpowernode.hibernate;import java.util.Date;import junit.framework.TestCase;import org.hibernate.ObjectNotFoundException; import org.hibernate.Session; import org.hibernate.Transaction;/*** Session測試類* * @author Longxuan* */ public class SessionTest extends TestCase {public void testEquals() {Session session = null;try {//獲取Sessionsession = HibernateUtils.getSession();// 開啟事務(wù)session.beginTransaction();System.out.println("\n\n\n\n");try {// 驗(yàn)證查不到數(shù)據(jù)時(shí),get返回null,load拋ObjectNotFoundException異常System.out.println(session.get(User.class, "123"));System.out.println(session.load(User.class, "123"));} catch (ObjectNotFoundException e) {System.out.println("load方法拋出ObjectNotFoundException異常");}System.out.println("\n\n");// 驗(yàn)證load返回實(shí)體類對象,而非代理對象{User user1 = (User) session.get(User.class,"4028981b41174a690141174a6c6d0003");User user2 = (User) session.load(User.class,"4028981b41174a690141174a6c6d0003");System.out.println("user1:" + user1.getClass().getSimpleName());System.out.println("user2:" + user2.getClass().getSimpleName());System.out.println("user1與 user2是否為同一對象:" + user1.equals(user2));}System.out.println("\n\n");session.clear();//清除Session// 驗(yàn)證get也可以返回代理類對象,而并不一定返回實(shí)體類對象// 同時(shí)驗(yàn)證了get方法先查找緩存(如果沒有輸出sql語句,則說明get查找了緩存){User user3 = (User) session.load(User.class,"4028981b41174a690141174a6c6d0003");User user4 = (User) session.get(User.class,"4028981b41174a690141174a6c6d0003");System.out.println("user3:" + user3.getClass().getSimpleName());System.out.println("user4:" + user4.getClass().getSimpleName());System.out.println("user3與 user4是否為同一對象:" + user3.equals(user4));}session.getTransaction().commit();} catch (Exception e) {e.printStackTrace();session.getTransaction().rollback();} finally {HibernateUtils.closeSession(session);}} }
運(yùn)行結(jié)果圖:
? ? ? 還有一個(gè)有趣的現(xiàn)象: User user5 = (User)session.load(User.class, "123"); System.out.println(user5.getId()); ? ? ? 運(yùn)行結(jié)果直接輸出 123 ? ? ? 從結(jié)果中也可以看出,前2句代碼,不會去執(zhí)行數(shù)據(jù)庫操作。因?yàn)閘oad后會在hibernate的一級緩存里存放一個(gè)map對象,該map的key就是Id的值,但是當(dāng)你getId()時(shí),它會去一級緩存里拿map的key值,正好找到了,所以不會再去執(zhí)行數(shù)據(jù)庫查詢。也不會報(bào)任何錯(cuò)。就有了以上的結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的一口一口吃掉Hibernate(二)——别被世俗蒙蔽了双眼:Hibernate中Session之get和load方法的真正区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 技术市场新技术
- 下一篇: [BZOJ4566][HAOI2016]