Hibernate緩衝應用的積累與總結,hibernate緩衝積累

來源:互聯網
上載者:User

Hibernate緩衝應用的積累與總結,hibernate緩衝積累

Hibernate緩衝一直比較難掌握,下面就分析和總結原因,相信你就會慢慢清楚了原來Hibernate緩衝也是可以輕鬆掌握的,但前提要求大家必須跟著動手去驗證一下,再用心體會,光看是沒有用的

一、hibernate一級緩衝(Session 層級的緩衝) 

  hibernate是一個線程對應一個session,一個線程可以看成一個使用者。也就是說session級緩衝(一級緩衝)只能給一個線程用,別的線程用不了,一級緩衝就是和線程綁定了。hibernate一級緩衝生命週期很短,和session生命週期一樣,一級緩衝也稱session級的緩衝或事務級緩衝。如果tb事務提交或復原了,我們稱session就關閉了,生命週期結束了。

  實驗1:體驗一級緩衝:(動手做做)

 //同一個session中,發出兩次load方法查詢Employee  emp= (Employee )session.load(Employee .class, 1);System.out.println( emp.getName());//不會發出查詢語句,load使用緩衝emp = (Employee )session.load(Employee .class, 1);System.out.println(emp.getName());

  第二次查詢第一次相同的資料,第二次load方法就是從緩衝裡取資料,不會發出sql語句到資料庫裡查詢。

  緩衝主要是用於查詢 ,hibernate的很多方法都是首先從緩衝中取資料如果沒有在從資料庫中擷取,以提升查詢效率如:get()/load()、iterate(),而且持久態的對象是會儲存在緩衝中的。例如:先save儲存實體物件,再用load方法查詢剛剛save的實體物件,則load方法不會發出sql語句到資料庫查詢的,而是到緩衝裡取資料,因為save方法也支援緩衝.

二、一級緩衝特徵及其應用:

  1.Session 層級的緩衝,它同session邦定。它的生命週期和session相同。Session消毀,它也同時消毀;

  2.兩個session 不能共用一級緩衝,因它會伴隨session的生命週期的建立和消毀;

  3.Session緩衝是實體層級的緩衝,就是只有在查詢對象層級的時候才使用,如果使用HQL和SQL是查詢屬性層級的,是不使用一級緩衝的!切記!!!

  4.iterate 查詢使用緩衝,會發出查詢Id的SQL和HQL語句,但不會發出查實體的,它查詢完會把相應的實體放到緩衝裡邊,一些實體查詢如果緩衝裡邊有,就從緩衝中查詢,但還是會發出查詢id的SQL和HQL語句。如果緩衝中沒有它會資料庫中查詢,然後將查詢到的實體一個一個放到緩衝中去,所以會有N+1問題出現。

  5.List()和iterate 查詢區別:(動手做做)

  使用iterate,list查詢實體物件*N+1問題,在預設情況下,使用query.iterate查詢,有可以能出現N+1問題

  所謂的N+1是在查詢的時候發出了N+1條sql語句1:首先發出一條查詢對象id列表的sqlN:

  根據id列表到緩衝中查詢,如果緩衝中不存在與之匹配的資料,那麼會根據id發出相應的sql語句list和iterate的區別?

  list每次都會發出sql語句,list會向緩衝中放入資料,而不利用緩衝中的資料

  iterate:在預設情況下iterate利用快取資料,但如果緩衝中不存在資料有可以能出現N+1問題

  6.Get()和load(),iterate方法都會使用一級緩衝,

  Get與load的區別? (動手做做)

  1. 對於get方法,hibernate會確認一下該id對應的資料是否存在,首先在session緩衝中尋找,然後在二級緩衝中尋找,還沒有就查詢資料庫,資料庫中沒有就返回null。

  2. load方法載入實體物件的時候,根據對應檔上類層級的lazy屬性的配置(預設為true),分情況討論:

    (1)若為true,則首先在Session緩衝中尋找,看看該id對應的對象是否存在,不存在則使用消極式載入,返回實體的代理類對象(該代理類為實體類的子類,由CGLIB動態產生)。等到具體使用該對象(除擷取OID以外)的時候,再查詢二級緩衝和資料庫,若仍沒發現合格記錄,則會拋出一個ObjectNotFoundException。

    (2)若為false,就跟get方法尋找順序一樣,只是最終若沒發現合格記錄,則會拋出一個ObjectNotFoundException。

  小結:

        1、get方法首先查詢session緩衝,沒有的話查詢二級緩衝,最後查詢資料庫;而load方法首先查詢session緩衝,沒有就建立代理,實際使用資料時才查詢二級緩衝和資料庫

        2、如果未能發現合格記錄,get方法返回null,而load方法會拋出一個ObjectNotFoundException。

        3、load使用代理消極式載入資料,而get方法往往返回有實體資料的對象

        使用:

        1、如果想對一個對象進行增刪改查之類,該使用load方法,效能提高,可以使用代理對象,省去了一次和資料庫互動的機會,當真正用到該對象的屬性時,才跟資料庫互動

        2、如果你想載入一個對象使用它的屬性,該使用get方法

  7.hiberate3 session 預存程序如下:

  例如 object 對象Session.save(object);

  這時候不會把資料放到資料庫,會先放到session緩衝中去,資料庫中沒有相應記錄,session.flush();才發SQL和HQL語句,資料庫中有了相應記錄,

  但是資料庫用select查不到,這是跟資料庫事物層級有關係。

  Session.beginTrransaction()。commit();

  事物提交後可以查詢到了。

  Session.flush()語句但是為什麼不寫呢,因為commit()會預設調用flush();  

三、管理一級緩衝

  無論何時,當你給save()、update()或 saveOrUpdate()方法傳遞一個對象時,或使用load()、 get()、list()、iterate() 或scroll()方法獲得一個對象時, 該對象都將被加入到Session的內部緩衝中。當隨後flush()方法被調用時,對象的狀態會和資料庫取得同步。 如果你不希望此同步操作發生,或者你正處理大量對象、需要對有效管理記憶體時,你可以調用evict() 方法,從一級緩衝中去掉這些對象及其集合。

ScrollableResult cats = sess.createQuery("from Cat as cat").scroll();while ( cats.next() ) {    Cat cat = (Cat) cats.get(0);    doSomethingWithACat(cat);    sess.evict(cat);}

Session還提供了一個contains()方法,用來判斷某個執行個體是否處於當前session的緩衝中。如若要把所有的對象從session緩衝中徹底清除,則需要調用Session.clear()。

四、Hibernate二級緩衝(sessionFactory層級緩衝)

  二級緩衝需要sessionFactory來管理,它是進初級的緩衝,所有人都可以使用,它是共用的。Hibernate二級緩衝支援對象緩衝、集合緩衝、查詢結果集緩衝,對於查詢結果集緩衝可選。

  二級緩衝比較複雜,一般用第三方產品。hibernate提供了一個簡單實現,用Hashtable做的,只能作為我們的測試使用,商用還是需要第三方產品。

  幾種優秀緩衝方案:

  1、Memcached 分布式緩衝系統 2、JBOSS CACHE   3 、EhCache   Ehcache 2.1起提供了針對Hibernate的JTA支援。 4、Infinispan 開來源資料網格平台

  使用緩衝,肯定是長時間不改變的資料,如果經常變化的資料放到緩衝裡就沒有太大意義了。因為經常變化,還是需要經常到資料庫裡查詢,那就沒有必要用緩衝了。hibernate做了一些最佳化,和一些第三方的緩衝產品做了整合。這裡採用EHCache緩衝產品。

  和EHCache二級緩衝產品整合:EHCache的jar檔案在hibernate的lib裡,我們還需要設定一系列的緩衝使用原則,需要一個設定檔ehcache.xml來配置。

  hibernate.cfg.xml 配置(動手做做) 

<!-- 開啟二級緩衝 -->  <property name="hibernate.cache.use_second_level_cache">true</property>  <!-- 開啟查詢快取 -->  <property name="hibernate.cache.use_query_cache">true</property>  <!-- 二級快取區域名的首碼 -->  <!--<property name="hibernate.cache.region_prefix">h3test</property>-->  <!-- 快取提供者  第三方產品-->  <property name="hibernate.cache.region.factory_class">  net.sf.ehcache.hibernate.EhCacheRegionFactory  </property>  <!-- 指定緩衝設定檔位置 -->  <property name="hibernate.cache.provider_configuration_file_resource_path">  ehcache.xml  </property>  <!-- 強制Hibernate以更人性化的格式將資料存入二級緩衝 -->  <property name="hibernate.cache.use_structured_entries">true</property>  <!-- Hibernate將收集有助於效能調節的統計資料 -->  <property name="hibernate.generate_statistics">true</property>   

  ehcache配置(ehcache.xml)(動手做做)

<?xml version="1.0" encoding="UTF-8"?>  <ehcache name="h3test">      <!--指定地區名--><defaultCache  maxElementsInMemory="100"      <!--緩衝在記憶體中的最大數目-->eternal="false"                          <!--緩衝是否持久-->timeToIdleSeconds="1200"     <!--當緩衝條目閑置n秒後銷毀-->timeToLiveSeconds="1200"    <!--當緩衝條目存活n秒後銷毀-->overflowToDisk="false">    <!--硬碟溢出--></defaultCache>  </ehcache>    

  實體唯讀緩衝

      唯讀緩衝 read only,不須要鎖與事務,因為緩衝自資料從資料庫載入後就不會改變。如果資料是唯讀,例如引用資料,那麼總是使用“read-only”策略,因為它是最簡單、最高效的策略,也是叢集安全的策略。是效能第一的策略

<hibernate-mapping>  <class name="com.ljb.entity.Voucher" table="Voucher">  <cache usage="read-only"/>       ……  </hibernate-mapping>

  二級緩衝測試代碼(動手做做)

Session session1 = sf.openSession();  Transaction t1 = session1.beginTransaction();  //確保資料庫中有標識符為1的Voucher Voucher voucher = (Vocher) session1.get(Vocher.class, 1);  //如果修改將報錯,唯讀緩衝不允許修改   //voucher.setName("aaa");  t1.commit();  session1.close();Session session2 = sf.openSession();  Transaction t2 = session2.beginTransaction();            voucher = (Vocher) session2.get(Vocher.class, 1);   //在二級緩衝中尋找結果,不會產出sql語句,不操作資料庫t2.commit();  session2.close();  sf.close();   

  唯讀緩衝不允許更新,將報錯Can't write to a readonly object。允許新增,( 新增直接添加到二級緩衝)

  讀寫緩衝 read write

  對緩衝的更新發生在資料庫事務完成後。緩衝需要支援鎖。在一個事務中更新資料庫,在這個事務成功完成後更新緩衝,並釋放鎖。鎖只是一種特定的緩衝值失效表述方式,在它獲得新資料庫值前阻止其他事務讀寫緩衝。那些事務會轉而直接讀取資料庫  

     實體讀/寫緩衝

<hibernate-mapping>  <class name="com.ljb.entity.Voucher" table="Voucher">  <cache usage="read-write"/>          ……  </hibernate-mapping> 

  二級緩衝測試代碼

Session session1 = sf.openSession();  Transaction t1 = session1.beginTransaction();  //確保資料庫中有標識符為1的Voucher Voucher voucher = (Vocher) session1.get(Vocher.class, 1);  //如果修改將報錯,唯讀緩衝不允許修改  voucher.setName("aaa");  t1.commit();  session1.close();        Session session2 = sf.openSession();  Transaction t2 = session2.beginTransaction();  voucher = (Vocher) session2.get(Vocher.class, 1);   //該條目已經被別的事務修改了,此時重新查詢一次資料庫   t2.commit();  session2.close();  sf.close();   

  允許更新,更新後自動同步到緩衝。允許新增,新增記錄後自動同步到緩衝。保證read committed隔離等級及可重複讀隔離等級(通過時間戳記實現)整個過程加鎖,如果當前事務的時間戳記早於二級緩衝中的條目的時間戳記,說明該條目已經被別的事務修改了,此時重新查詢一次資料庫,否則才使用快取資料,因此保證可重複讀隔離等級

  非嚴格讀寫緩衝 nonstrict read write

  在一個事務中更新資料庫,在這個事務完成前就清除緩衝,為了安全起見,無論事務成功與否,在事務完成後再次清除緩衝。既不需要支援緩衝鎖,也不需要支援事務。如果是緩衝叢集,“清除緩衝”調用會讓所有副本都失效,這通常被稱為“拉(pull)”更新策略。如果你的資料讀很多或者很少有並發緩衝訪問和更新,那麼可以使用“nonstrict-read-write”策略。感謝它的輕量級“拉”更新策略,它通常是效能第二好的策略。

  實體非嚴格讀/寫緩衝

<hibernate-mapping> <class name="com.ljb.entity.Voucher" table="Voucher"> <cache usage="nonstrict-read-write"/>      …… </hibernate-mapping>  

  測試代碼 略(我想大家會驗證了)

  驗證結果

  允許更新,更新後緩衝失效,需再查詢一次。 允許新增,新增記錄自動加到二級緩衝中。整個過程不加鎖,不保證。

  事務緩衝 transactional (一定要在JTA環境中)

  對緩衝和資料庫的更新被封裝在同一個JTA事務中,這樣緩衝與資料庫總是保持同步的。資料庫和緩衝都必須支援JTA。除非你真的想將緩衝更新和資料庫更新放在一個JTA事務裡,否則不要使用“transactional”策略,因為JTA需要漫長的兩階段交易認可處理,這導致它基本是效能最差的策略。

  需要特定緩衝的支援和JTA事務支援,此處不示範。 

  集合緩衝

  示範讀/寫緩衝樣本,和之前實體緩衝測試差不多,其他自測

<hibernate-mapping>    <class name="cn.javass.h3test.model.UserModel" table="TBL_USER">      <cache usage="read-write" />      <set name="vouchers" cascade="all" inverse="true" lazy="false">        <cache usage="read-write"/>        <key column="fk_user_id"/>        <one-to-many class="cn.ljb.entity.Voucher"/>      </set>    </class>  </hibernate-mapping>    
SessionFactory sf =   new Configuration().configure().buildSessionFactory();  Session session1 = sf.openSession();  Transaction t1 = session1.beginTransaction();   //確保資料庫中有標識符為1的UserModel  UserModel user = (UserModel) session1.get(UserModel.class, 1);  user.getVouchers();  t1.commit();  session1.close();        Session session2 = sf.openSession();  Transaction t2 = session2.beginTransaction();  user = (UserModel) session2.get(UserModel.class, 1);  user.getVouchers();  t2.commit();  session2.close();  sf.close();  

  測試結論:   

  和實體並發策略有相同含義; 但集合緩衝只緩衝集合元素的標識符,在二級緩衝中只存放相應實體的標識符,然後再通過標識符去二級緩衝尋找相應的實體最後組合為集合返回。

  查詢快取 (動手做做)

  1、保證全域配置中有開啟了查詢快取。

  2、修改FarmModel.hbm.xml,添加如下紅色部分配置,表示實體緩衝並讀/寫

<hibernate-mapping>      <class name="com.lijb.entity.Voucher" table="voucher">          <cache usage="read-write"/>           ……  </hibernate-mapping>   

  3、測試代碼

SessionFactory sf =   .new Configuration().configure().buildSessionFactory();  Session session1 = sf.openSession();  Transaction t1 = session1.beginTransaction();  Query query = session1.createQuery("fromVoucher");  //即使全域開啟了查詢快取,此處也是必須的  query.setCacheable(true);  List<Voucher> voucherList = query.list();  t1.commit();  session1.close();  Session session2 = sf.openSession();  Transaction t2 = session2.beginTransaction();  query = session2.createQuery("from Voucher");  //即使全域開啟了查詢快取,此處也是必須的  query.setCacheable(true);  voucherList = query.list();  t2.commit();  session2.close();  sf.close();  

  結論:

  和實體並發策略有相同含義; 和集合緩衝類似,只緩衝集合元素的標識符,在二級緩衝中只存放相應實體的標識符,然後再通過標識符 去二級緩衝尋找相應的實體最後組合為集合返回。

      什麼時候需要查詢快取?

      大多數時候無法從結果集快取獲益。必須知道:每隔多久重複執行同一查詢。對於那些查詢非常多但插入、刪除、更新非常少的應用程式來說,查詢快取可提升效能。但寫入多查詢少的沒有用,總失效。

  管理二級緩衝

  對於二級緩衝來說,在SessionFactory中定義了許多方法, 清除緩衝中執行個體、整個類、集合執行個體或者整個集合。

sessionFactory.evict(Cat.class, catId); //evict a particular CatsessionFactory.evict(Cat.class);  //evict all CatssessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittenssessionFactory.evictCollection("Cat.kittens"); //evict all kitten collectionssessionFactory.evictQueries()//evict all queries//CacheMode參數用於控制具體的Session如何與二級緩衝進行互動。//CacheMode.NORMAL - 從二級緩衝中讀、寫資料。//CacheMode.GET - 從二級緩衝中讀取資料,僅在資料更新時對二級緩衝寫資料。//CacheMode.PUT - 僅向二級緩衝寫資料,但不從二級緩衝中讀資料。//CacheMode.REFRESH - 僅向二級緩衝寫資料,但不從二級緩衝中讀資料。通過 hibernate.cache.use_minimal_puts的設定,強制二級緩衝從資料庫中讀取資料,重新整理緩衝內容

  監控二級緩衝

  如若需要查看二級緩衝或查詢快取地區的內容,你可以使用統計(Statistics) API。通過sessionFactory.getStatistics();擷取Hibernate統計資訊。此時,你必須手工開啟統計選項。

 hibernate.generate_statistics true hibernate.cache.use_structured_entries true
 五、總結

  不要想當然的以為緩衝一定能提高效能,僅僅在你能夠駕馭它並且條件合適的情況下才是這樣的。hibernate的二級緩衝限制還是比較多的,不方便用jdbc可能會大大的降低更新效能。在不瞭解原理的情況下亂用,可能會有1+N的問題。不當的使用還可能導致讀出髒資料。

  如果受不了hibernate的諸多限制,那麼還是自己在應用程式的層面上做緩衝吧。

  在越高的層面上做緩衝,效果就會越好。就好像儘管磁碟有緩衝,資料庫還是要實現自己的緩衝,儘管資料庫有緩衝,咱們的應用程式還是要做緩衝。因為底層的緩衝它並不知道高層要用這些資料幹什麼,只能做的比較通用,而高層可以有針對性的實現緩衝,所以在更高的層級上做緩衝,效果也要好些。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.