對於Hibernate這類ORM而言,緩存顯的尤其重要,它是持久層性能提高的關鍵.簡單來說Hibernate就是對JDBC進行封裝,以實現內部狀態的管理,OR關係的映射等,但隨之帶來的就是數據訪問效率的下降,和性能的降低,而緩存就是彌補這一缺點的重要方法.
緩存就是數據庫數據在內存中的臨時容器,包括數據庫數據在內存中的臨時拷貝,它位於數據庫與數據庫訪問層中間.ORM在查詢數據時首先會根據自身的緩存管 理策略,在緩存中查找相關數據,如發現所需的數據,則直接將此數據做爲結果加以利用,從而避免了數據庫調用性能的開銷.而相對內存操做而言,數據庫調用是 一個代價高昂的過程.
通常來說ORM中的緩存分爲如下幾類:
1:事務級緩存:即在當前事務範圍內的數據緩存.就Hibernate來說,事務級緩存是基於 Session的生命週期實現的,每一個Session內部會存在一個數據緩存,它隨着 Session的建立而存在,隨着Session的銷燬而滅亡,所以也稱爲Session Level Cache.
2:應用級緩存:即在某個應用中或應用中某個獨立數據庫訪問子集中的共享緩存,此緩存可由多個事務 共享(數據庫事務或應用事務),事務之間的緩存共享策略與應用的事務隔離機制密切相關.在Hibernate中,應用級緩存由 SessionFactory實現,全部由一個SessionFactory建立的 Session實例共享此緩存,所以也稱爲SessionFactory Level Cache.
3:分佈式緩存:即在多個應用實例,多個JVM間共享的緩存策略.分佈式緩存由多個應用級緩存實例組成,經過某種遠程機制(RMI,JMS)實現各個緩存實例間的數據同步,任何一個實例的數據修改,將致使整個集羣間的數據狀態同步.html
Hibernate的一,二級緩存策略:java
Hibernate中提供了兩級Cache,第一級別的緩存是Session級別的緩存,它是屬於事務範圍的緩存。這一級別的緩存由hibernate管理的,通常狀況下無需進行干預;第二級別的緩存是SessionFactory級別的緩存,它是屬於進程範圍或羣集範圍的緩存。這一級別的緩存能夠進行配置和更改,而且能夠動態加載和卸載,屬於多事務級別,要防止事務併發性。node
緩存是以map的形式進行存儲的(key-id,value-object)web
一級緩存(Session):sql
事務範圍,每一個事務(Session)都有單獨的第一級緩存.數據庫
一級緩存的管理:當應用程序調用Session的save()、update()、saveOrUpdate()、get()或load(),以及調用查 詢接口的 list()、iterate()--(用的是n+1次查詢,先查id)或filter()方法時,若是在Session緩存中還不存在相應的對 象,Hibernate就會把該對象加入到第一級緩存中。當清理緩存時,Hibernate會根據緩存中對象的狀態變化來同步更新數據庫。 Session爲應用程序提供了兩個管理緩存的方法: evict(Object obj):從緩存中清除參數指定的持久化對象。 clear():清空緩存中全部持久化對象,flush():使緩存與數據庫同步。緩存
當查詢相應的字段如(name),而不是對象時,不支持緩存。session
二級緩存(SessionFactory):併發
Hibernate的二級緩存策略的通常過程以下:oracle
1:條件查詢的時候,老是發出一條select * from table_name where …. (選擇全部字段)這樣的SQL句查詢數據庫,一次得到全部的數據對象(這個問題要考慮,若是你查詢十萬條數據時,內存不是被佔用)。
2:把得到的全部數據對象根據ID放入到第二級緩存中。
3: 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,若是配置了二級緩存,那麼從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
4:刪除、更新、增長數據的時候,同時更新緩存。
Hibernate的二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無做用。爲此,Hibernate提供了針對條件查詢的Query Cache。
Q:什麼樣的數據適合存放到第二級緩存中?
1.不多被修改的數據
2.不是很重要的數據,容許出現偶爾併發的數據
3.不會被併發訪問的數據
4.參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其餘類的實例引用,實例極少或者歷來不會被修改。
不適合存放到第二級緩存的數據?
1 常常被修改的數據
2 財務數據,絕對不容許出現併發
3 與其餘應用共享的數據。
經常使用的緩存插件 Hibernater 的二級緩存是一個插件,下面是幾種經常使用的緩存插件:
EhCache:可做爲進程範圍的緩存,存放數據的物理介質能夠是內存或硬盤,對Hibernate的查詢緩存提供了支持。
OSCache:可做爲進程範圍的緩存,存放數據的物理介質能夠是內存或硬盤,提供了豐富的緩存數據過時策略,對Hibernate的查詢緩存提供了支持。
SwarmCache:可做爲羣集範圍內的緩存,但不支持Hibernate的查詢緩存。
JBossCache:可做爲羣集範圍內的緩存,支持事務型併發訪問策略,對Hibernate的查詢緩存提供了支持。
配置二級緩存的主要步驟:
1 選擇須要使用二級緩存的持久化類,設置它的命名緩存的併發訪問策略。這是最值得認真考慮的步驟。
2 選擇合適的緩存插件,而後編輯該插件的配置文件。
hibernate提供的一級緩存
hibernate是一個線程對應一個session,一個線程能夠當作一個用戶。也就是說session級緩存(一級緩存)只能給一個線程用,別的線程用不了,一級緩存就是和線程綁定了。
hibernate一級緩存生命週期很短,和session生命週期同樣,一級緩存也稱session級的緩存或事務級緩存。若是tb事務提交或回滾了,咱們稱session就關閉了,生命週期結束了。
緩存和鏈接池的區別:緩 存和池都是放在內存裏,實現是同樣的,都是爲了提升性能的。但有細微的差異,池是重量級的,裏面的數據是同樣的,好比一個池裏放100個 Connection鏈接對象,這個100個都是同樣的。緩存裏的數據,每一個都不同。好比讀取100條數據庫記錄放到緩存裏,這100條記錄都不同。
緩存主要是用於查詢
//同一個session中,發出兩次load方法查詢 Student student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); //不會發出查詢語句,load使用緩存 student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); |
第二次查詢第一次相同的數據,第二次load方法就是從緩存裏取數據,不會發出sql語句到數據庫裏查詢。
//同一個session,發出兩次get方法查詢 Student student = (Student)session.get(Student.class, 1); System.out.println("student.name=" + student.getName()); //不會發出查詢語句,get使用緩存 student = (Student)session.get(Student.class, 1); System.out.println("student.name=" + student.getName()); |
第二次查詢第一次相同的數據,第二次不會發出sql語句查詢數據庫,而是到緩存裏取數據。
//同一個session,發出兩次iterate查詢實體對象 Iterator iter = session.createQuery ("from Student s where s.id<5").iterate(); while (iter.hasNext()) { Student student = (Student)iter.next(); System.out.println(student.getName()); } System.out.println("--------------------------------------"); //它會發出查詢id的語句,但不會發出根據id查詢學生的語句,由於iterate使用緩存 iter = session.createQuery("from Student s where s.id<5").iterate(); while (iter.hasNext()) { Student student = (Student)iter.next(); System.out.println(student.getName()); } |
一說到iterater查詢就要馬上想起:iterater查詢在沒有緩存的狀況下會有N+1的問題。
執行上面代碼查看控制檯的sql語句,第一次iterate查詢會發出N+1條sql語句,第一條sql語句查詢全部的id,而後根據id查詢實體對象,有N個id就發出N條語句查詢實體。
第二次iterate查詢,卻只發一條sql語句,查詢全部的id,而後根據id到緩存裏取實體對象,再也不發sql語句到數據庫裏查詢了。
//同一個session,發出兩次iterate查詢,查詢普通屬性 Iterator iter = session.createQuery( "select s.name from Student s where s.id<5").iterate(); while (iter.hasNext()) { String name = (String)iter.next(); System.out.println(name); } System.out.println("--------------------------------------"); //iterate查詢普通屬性,一級緩存不會緩存,因此發出查詢語句 //一級緩存是緩存實體對象的 iter = session.createQuery ("select s.name from Student s where s.id<5").iterate(); while (iter.hasNext()) { String name = (String)iter.next(); System.out.println(name); } |
執行代碼看控制檯sql語句,第一次發出N+1條sql語句,第二次仍是發出了N+1條sql語句。由於一級緩存只緩存實體對象,tb不會緩存普通屬性,因此第二次仍是發出sql查詢語句。
//兩個session,每一個session發出一個load方法查詢實體對象 try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } 第二個session調用load方法 try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); //會發出查詢語句,session間不能共享一級緩存數據 //由於他會伴隨着session的消亡而消亡 System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } |
第一個session的 load方法會發出sql語句查詢實體對象,第二個session的load方法也會發出sql語句查詢實體對象。由於session間不能共享一級緩存 的數據,因此第二個session的load方法查詢相同的數據仍是要到數據庫中查詢,由於它找不到第一個session裏緩存的數據。
//同一個session,先調用save方法再調用load方法查詢剛剛save的數據 Student student = new Student(); student.setName("張三"); //save方法返回實體對象的id Serializable id = session.save(student); student = (Student)session.load(Student.class, id); //不會發出查詢語句,由於save支持緩存 System.out.println("student.name=" + student.getName()); |
先save保存實體對象,再用load方法查詢剛剛save的實體對象,則load方法不會發出sql語句到數據庫查詢的,而是到緩存裏取數據,由於save方法也支持緩存。固然前提是同一個session。
//大批量的數據添加 for (int i=0; i<100; i++) { Student student = new Student(); student.setName("張三" + i); session.save(student); //每20條更新一次 if (i % 20 == 0) { session.flush(); //清除緩存的內容 session.clear(); } } |
大批量數據添加時,會形成內存溢出的,由於save方法支持緩存,每save一個對象就往緩存裏放,若是對象足夠多內存確定要溢出。通常的作法是先判斷一下save了多少個對象,若是save了20個對象就對緩存手動的清理緩存,這樣就不會形成內存溢出。
注意:清理緩存前,要手動調用flush方法同步到數據庫,不然save的對象就沒有保存到數據庫裏。
注意:大批量數據的添加仍是不要使用hibernate,這是hibernate弱項。可使用jdbc(速度也不會太快,只是比hibernate好一點),或者使用工具產品來實現,好比oracle的Oracle SQL Loader,導入數據特別快。
Hibernate 二級緩存
二級緩存須要sessionFactory來管理,它是進初級的緩存,全部人均可以使用,它是共享的。
二級緩存比較複雜,通常用第三方產品。hibernate提供了一個簡單實現,用Hashtable作的,只能做爲咱們的測試使用,商用仍是須要第三方產品。
使用緩存,確定是長時間不改變的數據,若是常常變化的數據放到緩存裏就沒有太大意義了。由於常常變化,仍是須要常常到數據庫裏查詢,那就沒有必要用緩存了。
hibernate作了一些優化,和一些第三方的緩存產品作了集成。老師採用EHCache緩存產品。
和EHCache二級緩存產品集成:EHCache的jar文件在hibernate的lib裏,咱們還須要設置一系列的緩存使用策略,須要一個配置文件ehcache.xml來配置。這個文件放在類路徑下。
//默認配置,全部的類都遵循這個配置 <defaultCache //緩存裏能夠放10000個對象 maxElementsInMemory="10000" //過不過時,若是是true就是永遠不過時 eternal="false" //一個對象被訪問後多長時間尚未訪問就失效(120秒尚未再次訪問就失效) timeToIdleSeconds="120" //對象存活時間(120秒),若是設置永不過時,這個就沒有必要設了 timeToLiveSeconds="120" //溢出的問題,若是設成true,緩存裏超過10000個對象就保存到磁盤裏 overflowToDisk="true" /> |
咱們也能夠對某個對象單獨配置:
<cache name="com.bjpowernode.hibernate.Student" maxElementsInMemory="100" eternal="false" timeToIdleSeconds="10000" timeToLiveSeconds="10000" overflowToDisk="true" /> |
還須要在hibernate.cfg.xml配置文件配置緩存,讓hibernate知道咱們使用的是那個二級緩存。
<!-- 配置緩存提供商 --> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider</property> <!-- 啓用二級緩存,這也是它的默認配置 --> <property name="hibernate.cache.use_second_level_cache"> true</property> |
啓用二級緩存的配置能夠不寫的,由於默認就是true開啓二級緩存。
必須還手動指定那些實體類的對象放到緩存裏在hibernate.cfg.xml裏:
//在<sessionfactory>標籤裏,在<mapping>標籤後配置 <class-cache class="com.bjpowernode.hibernate.Student" usage="read-only"/> |
或者在實體類映射文件裏:
//在<class>標籤裏,<id>標籤前配置 <cache usage="read-only"/> |
usage屬性表示使用緩存的策略,通常優先使用read-only,表示若是這個數據放到緩存裏了,則不容許修改,若是修改就會報錯。這就要注意咱們放入緩存的數據不容許修改。由於放緩存裏的數據常常修改,也就沒有必要放到緩存裏。
使用read-only 策略效率好,由於不能改緩存。可是可能會出現髒數據的問題,這個問題解決方法只能依賴緩存的超時,好比上面咱們設置了超時爲120秒,120後就能夠對緩 存裏對象進行修改,而在120秒以內訪問這個對象可能會查詢髒數據的問題,由於咱們修改對象後數據庫裏改變了,而緩存卻不能改變,這樣形成數據不一樣步,也 就是髒數據的問題。
第二種緩存策略read-write,當持久對象發生變化,緩存裏就會跟着變化,數據庫中也改變了。這種方式須要加解鎖,效率要比第一種慢。
還有兩種策略,請看hibernate文檔,最經常使用仍是第一二種策略。
二級緩存測試代碼演示:注意上面咱們講的兩個session分別調用load方法查詢相同的數據,第二個session的load方法仍是發了sql語句到數據庫查詢數據,這是由於一級緩存只在當前session中共享,也就是說一級緩存不能跨session訪問。
//開啓二級緩存,二級緩存是進程級的緩存,能夠共享 //兩個session分別調用load方法查詢相同的實體對象 try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); //不會發出查詢語句,由於配置二級緩存,session能夠共享二級緩存中的數據 //二級緩存是進程級的緩存 System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } |
若是開啓了二級緩存,那麼第二個session調用的load方法查詢第一次查詢的數據,是不會發出sql語句查詢數據庫的,而是去二級緩存中取數據。
//開啓二級緩存 //兩個session分別調用get方法查詢相同的實體對象 try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.get(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.get(Student.class, 1); //不會發出查詢語句,由於配置二級緩存,session能夠共享二級緩存中的數據 //二級緩存是進程級的緩存 System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } |
注意:二級緩存必須讓sessionfactory管理,讓sessionfactory來清除二級緩存。sessionFactory.evict(Student.class);//清除二級緩存中全部student對象,sessionFactory.evict(Student.class,1);//清除二級緩存中id爲1的student對象。
若是在第一個session調用load或get方法查詢數據後,把二級緩存清除了,那麼第二個session調用load或get方法查詢相同的數據時,仍是會發出sql語句查詢數據庫的,由於緩存裏沒有數據只能到數據庫裏查詢。
咱們查詢數據後會默認自動的放到二級和一級緩存裏,若是咱們想查詢的數據不放到緩存裏,也是能夠的。也就是說咱們能夠控制一級緩存和二級緩存的交換。
session.setCacheMode(CacheMode.IGNORE);禁止將一級緩存中的數據往二級緩存裏放。
仍是用上面代碼測試,在 第一個session調用load方法前,執行session.setCacheMode(CacheMode.IGNORE);這樣load方法查詢的 數據不會放到二級緩存裏。那麼第二個session執行load方法查詢相同的數據,會發出sql語句到數據庫中查詢,由於二級緩存裏沒有數據,一級緩存 由於不一樣的session不能共享,因此只能到數據庫裏查詢。
上面咱們講過大批量的數 據添加時可能會出現溢出,解決辦法是每當天就20個對象後就清理一次一級緩存。若是咱們使用了二級緩存,光清理一級緩存是不夠的,還要禁止一二級緩存交 互,在save方法前調用session.setCacheMode(CacheMode.IGNORE)。
二級緩存也不會存放普通屬性的查詢數據,這和一級緩存是同樣的,只存放實體對象。session級的緩存對性能的提升沒有太大的意義,由於生命週期過短了。
Hibernate 查詢緩存
一級緩存和二級緩存都只是存放實體對象的,若是查詢實體對象的普通屬性的數據,只能放到查詢緩存裏,查詢緩存還存放查詢實體對象的id。
查詢緩存的生命週期不肯定,當它關聯的表發生修改,查詢緩存的生命週期就結束。這裏表的修改指的是經過hibernate修改,並非經過數據庫客戶端軟件登錄到數據庫上修改。
hibernate的查詢緩存默認是關閉的,若是要使用就要到hibernate.cfg.xml文件裏配置:
<property name="hibernate.cache.use_query_cache">true</property> |
而且必須在程序中手動啓用查詢緩存,在query接口中的setCacheable(true)方法來啓用。
//關閉二級緩存,沒有開啓查詢緩存,採用list方法查詢普通屬性 //同一個sessin,查詢兩次 List names = session.createQuery("select s.name from Student s") .list(); for (int i=0; i<names.size(); i++) { String name = (String)names.get(i); System.out.println(name); } System.out.println("-----------------------------------------"); //會發出sql語句 names = session.createQuery("select s.name from Student s") .setCacheable(true) .list(); for (int i=0; i<names.size(); i++) { String name = (String)names.get(i); System.out.println(name); } |
上面代碼運行,因爲沒有使用查詢緩存,而1、二級緩存不會緩存普通屬性,因此第二次查詢仍是會發出sql語句到數據庫中查詢。
如今開啓查詢緩存,關閉二級緩存,而且在第一次的list方法前調用setCacheable(true),而且第二次list查詢前也調用這句代碼,能夠寫出下面這樣:
List names = session.createQuery("select s.name from Student s") .setCacheable(true) .list(); |
其它代碼不變,運行代碼後發現第二次list查詢普通屬性沒有發出sql語句,也就是說沒有到數據庫中查詢,而是到查詢緩存中取數據。
//開啓查詢緩存,關閉二級緩存,採用list方法查詢普通屬性 //在兩個session中調用list方法 try { session = HibernateUtils.getSession(); session.beginTransaction(); List names = session.createQuery("select s.name from Student s") .setCacheable(true) .list(); for (int i=0; i<names.size(); i++) { String name = (String)names.get(i); System.out.println(name); } session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } System.out.println("----------------------------------------"); try { session = HibernateUtils.getSession(); session.beginTransaction(); //不會發出查詢語句,由於查詢緩存和session的生命週期沒有關係 List names = session.createQuery("select s.name from Student s") .setCacheable(true) .list(); for (int i=0; i<names.size(); i++) { String name = (String)names.get(i); System.out.println(name); } session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } |
運行結果是第二個session發出的list方法查詢普通屬性,沒有發出sql語句到數據庫中查詢,而是到查詢緩存裏取數據,這說明查詢緩存和session生命週期沒有關係。
//開啓緩存,關閉二級緩存,採用iterate方法查詢普通屬性 //在兩個session中調用iterate方法查詢 |
運行結果是第二個session的iterate方法仍是發出了sql語句查詢數據庫,這說明iterate迭代查詢普通屬性不支持查詢緩存。
//關閉查詢緩存,關閉二級緩存,採用list方法查詢實體對象 //在兩個session中調用list方法查詢 |
運行結果第一個session調用list方法查詢實體對象會發出sql語句查詢數據,由於關閉了二級緩存,因此第二個session調用list方法查詢實體對象,仍是會發出sql語句到數據庫中查詢。
//開啓查詢緩存,關閉二級緩存 //在兩個session中調用list方法查詢實體對象 |
運行結果第一個 session調用list方法查詢實體對象會發出sql語句查詢數據庫的。第二個session調用list方法查詢實體對象,卻發出了不少sql語句 查詢數據庫,這跟N+1的問題是同樣的,發出了N+1條sql語句。爲何會出現這樣的狀況呢?這是由於咱們如今查詢的是實體對象,查詢緩存會把第一次查詢的實體對象的id放到緩存裏,當第二個session再次調用list方法時,它會到查詢緩存裏把id一個一個的拿出來,而後到相應的緩存裏找(先找一級緩存找不到再找二級緩存),若是找到了就返回,若是仍是沒有找到,則會根據一個一個的id到數據庫中查詢,因此一個id就會有一條sql語句。
注意:若是配置了二級緩存,則第一次查詢實體對象後,會往一級緩存和二級緩存裏都存放。若是沒有二級緩存,則只在一級緩存裏存放。(一級緩存不能跨session共享)
//開啓查詢緩存,開啓二級緩存 //在兩個session中調用list方法查詢實體對象 |
運行結果是第一個 session調用list方法會發出sql語句到數據庫裏查詢實體對象,由於配置了二級緩存,則實體對象會放到二級緩存裏,由於配置了查詢緩存,則實體 對象全部的id放到了查詢緩存裏。第二個session調用list方法不會發出sql語句,而是到二級緩存裏取數據。
查詢緩存意義不大,查詢 緩存說白了就是存放由list方法或iterate方法查詢的數據。咱們在查詢時不多出現徹底相同條件的查詢,這也就是命中率低,這樣緩存裏的數據老是變 化的,因此說意義不大。除非是屢次查詢都是查詢相同條件的數據,也就是說返回的結果老是同樣,這樣配置查詢緩存纔有意義。
posted on 2012-06-05 12:44 chen11-1 閱讀(14054) 評論(9) 編輯 收藏 所屬分類: java
Feedback
# re: Hibernate 全部緩存機制詳解 2013-11-01 13:56 叮叮噹噹
# re: Hibernate 全部緩存機制詳解 2013-12-12 18:34 hubery
不錯,寫得很詳細,很好,是很好的學習榜樣!!! 回覆 更多評論
# re: Hibernate 全部緩存機制詳解 2014-01-14 16:30 我來了
# re: Hibernate 全部緩存機制詳解 2014-02-14 10:05 很快解決
# re: Hibernate 全部緩存機制詳解 2014-02-28 09:49 hiber
# re: Hibernate 全部緩存機制詳解 2014-03-05 13:21 rlf
# re: Hibernate 全部緩存機制詳解 2014-04-03 18:47 無名氏
# re: Hibernate 全部緩存機制詳解 2014-04-07 20:50 wanjinxiong
是否是寫錯了,上面所說的一級緩存一個說第二次查詢是到緩存中取數據!下面怎麼又說第二次查詢仍是要到數據庫中取。由於session間不能共享一級緩存 的數據,因此第二個session的load方法查詢相同的數據仍是要到數據庫中查詢,由於它找不到第一個session裏緩存的數據