国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Hibernate緩存應(yīng)用的積累與總結(jié)

2019-11-14 22:43:27
字體:
供稿:網(wǎng)友
Hibernate緩存應(yīng)用的積累與總結(jié) 2015-01-27 09:36 by 杰瑞教育, ... 閱讀, ... 評論, 收藏, 編輯

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

目錄:

一、hibernate一級緩存(session 級別的緩存)

二、一級緩存特征及其應(yīng)用

三、管理一級緩存

四、Hibernate二級緩存(sessionFactory級別緩存)

五、總結(jié)

一、hibernate一級緩存(Session 級別的緩存)

  hibernate是一個線程對應(yīng)一個session,一個線程可以看成一個用戶。也就是說session級緩存(一級緩存)只能給一個線程用,別的線程用不了,一級緩存就是和線程綁定了。hibernate一級緩存生命周期很短,和session生命周期一樣,一級緩存也稱session級的緩存或事務(wù)級緩存。如果tb事務(wù)提交或回滾了,我們稱session就關(guān)閉了,生命周期結(jié)束了。

  實(shí)驗(yàn)1:體驗(yàn)一級緩存:(動手做做)

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

  第二次查詢第一次相同的數(shù)據(jù),第二次load方法就是從緩存里取數(shù)據(jù),不會發(fā)出sql語句到數(shù)據(jù)庫里查詢。

  緩存主要是用于查詢 ,hibernate的很多方法都是首先從緩存中取數(shù)據(jù)如果沒有在從數(shù)據(jù)庫中獲取,以提升查詢效率如:get()/load()、iterate(),而且持久態(tài)的對象是會存儲在緩存中的。例如:先save保存實(shí)體對象,再用load方法查詢剛剛save的實(shí)體對象,則load方法不會發(fā)出sql語句到數(shù)據(jù)庫查詢的,而是到緩存里取數(shù)據(jù),因?yàn)閟ave方法也支持緩存.

二、一級緩存特征及其應(yīng)用:

  1.Session 級別的緩存,它同session邦定。它的生命周期和session相同。Session消毀,它也同時消毀;

  2.兩個session 不能共享一級緩存,因它會伴隨session的生命周期的創(chuàng)建和消毀;

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

  4.iterate 查詢使用緩存,會發(fā)出查詢Id的SQL和HQL語句,但不會發(fā)出查實(shí)體的,它查詢完會把相應(yīng)的實(shí)體放到緩存里邊,一些實(shí)體查詢?nèi)绻彺胬镞呌校蛷木彺嬷胁樵儯€是會發(fā)出查詢id的SQL和HQL語句。如果緩存中沒有它會數(shù)據(jù)庫中查詢,然后將查詢到的實(shí)體一個一個放到緩存中去,所以會有N+1問題出現(xiàn)。

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

  使用iterate,list查詢實(shí)體對象*N+1問題,在默認(rèn)情況下,使用query.iterate查詢,有可以能出現(xiàn)N+1問題

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

  根據(jù)id列表到緩存中查詢,如果緩存中不存在與之匹配的數(shù)據(jù),那么會根據(jù)id發(fā)出相應(yīng)的sql語句list和iterate的區(qū)別?

  list每次都會發(fā)出sql語句,list會向緩存中放入數(shù)據(jù),而不利用緩存中的數(shù)據(jù)

  iterate:在默認(rèn)情況下iterate利用緩存數(shù)據(jù),但如果緩存中不存在數(shù)據(jù)有可以能出現(xiàn)N+1問題

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

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

  1. 對于get方法,hibernate會確認(rèn)一下該id對應(yīng)的數(shù)據(jù)是否存在,首先在session緩存中查找,然后在二級緩存中查找,還沒有就查詢數(shù)據(jù)庫,數(shù)據(jù)庫中沒有就返回null。

  2. load方法加載實(shí)體對象的時候,根據(jù)映射文件上類級別的lazy屬性的配置(默認(rèn)為true),分情況討論:

   (1)若為true,則首先在Session緩存中查找,看看該id對應(yīng)的對象是否存在,不存在則使用延遲加載,返回實(shí)體的代理類對象(該代理類為實(shí)體類的子類,由CGLIB動態(tài)生成)。等到具體使用該對象(除獲取OID以外)的時候,再查詢二級緩存和數(shù)據(jù)庫,若仍沒發(fā)現(xiàn)符合條件的記錄,則會拋出一個ObjectNotFoundException。

   (2)若為false,就跟get方法查找順序一樣,只是最終若沒發(fā)現(xiàn)符合條件的記錄,則會拋出一個ObjectNotFoundException。

  小結(jié):

1、get方法首先查詢session緩存,沒有的話查詢二級緩存,最后查詢數(shù)據(jù)庫;而load方法首先查詢session緩存,沒有就創(chuàng)建代理,實(shí)際使用數(shù)據(jù)時才查詢二級緩存和數(shù)據(jù)庫

2、如果未能發(fā)現(xiàn)符合條件的記錄,get方法返回null,而load方法會拋出一個ObjectNotFoundException。

3、load使用代理延遲加載數(shù)據(jù),而get方法往往返回有實(shí)體數(shù)據(jù)的對象

使用:

1、如果想對一個對象進(jìn)行增刪改查之類,該使用load方法,性能提高,可以使用代理對象,省去了一次和數(shù)據(jù)庫交互的機(jī)會,當(dāng)真正用到該對象的屬性時,才跟數(shù)據(jù)庫交互

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

  7.hiberate3 session 存儲過程如下:

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

  這時候不會把數(shù)據(jù)放到數(shù)據(jù)庫,會先放到session緩存中去,數(shù)據(jù)庫中沒有相應(yīng)記錄,session.flush();才發(fā)SQL和HQL語句,數(shù)據(jù)庫中有了相應(yīng)記錄,

  但是數(shù)據(jù)庫用select查不到,這是跟數(shù)據(jù)庫事物級別有關(guān)系。

  Session.beginTrransaction()。commit();

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

  Session.flush()語句但是為什么不寫呢,因?yàn)閏ommit()會默認(rèn)調(diào)用flush();

三、管理一級緩存

  無論何時,當(dāng)你給save()、update()或 saveOrUpdate()方法傳遞一個對象時,或使用load()、 get()、list()、iterate() 或scroll()方法獲得一個對象時, 該對象都將被加入到Session的內(nèi)部緩存中。當(dāng)隨后flush()方法被調(diào)用時,對象的狀態(tài)會和數(shù)據(jù)庫取得同步。 如果你不希望此同步操作發(fā)生,或者你正處理大量對象、需要對有效管理內(nèi)存時,你可以調(diào)用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()方法,用來判斷某個實(shí)例是否處于當(dāng)前session的緩存中。如若要把所有的對象從session緩存中徹底清除,則需要調(diào)用Session.clear()。

四、Hibernate二級緩存(sessionFactory級別緩存)

  二級緩存需要sessionFactory來管理,它是進(jìn)初級的緩存,所有人都可以使用,它是共享的。Hibernate二級緩存支持對象緩存、集合緩存、查詢結(jié)果集緩存,對于查詢結(jié)果集緩存可選。

  二級緩存比較復(fù)雜,一般用第三方產(chǎn)品。hibernate提供了一個簡單實(shí)現(xiàn),用Hashtable做的,只能作為我們的測試使用,商用還是需要第三方產(chǎn)品。

  幾種優(yōu)秀緩存方案:

  1、Memcached 分布式緩存系統(tǒng) 2、JBOSS CACHE 3 、EhCache Ehcache 2.1起提供了針對Hibernate的JTA支持。 4、Infinispan 開源數(shù)據(jù)網(wǎng)格平臺

  使用緩存,肯定是長時間不改變的數(shù)據(jù),如果經(jīng)常變化的數(shù)據(jù)放到緩存里就沒有太大意義了。因?yàn)榻?jīng)常變化,還是需要經(jīng)常到數(shù)據(jù)庫里查詢,那就沒有必要用緩存了。hibernate做了一些優(yōu)化,和一些第三方的緩存產(chǎn)品做了集成。這里采用EHCache緩存產(chǎn)品。

  和EHCache二級緩存產(chǎn)品集成:EHCache的jar文件在hibernate的lib里,我們還需要設(shè)置一系列的緩存使用策略,需要一個配置文件ehcache.xml來配置。

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

<!-- 開啟二級緩存 -->  <property name="hibernate.cache.use_second_level_cache">true</property>  <!-- 開啟查詢緩存 -->  <property name="hibernate.cache.use_query_cache">true</property>  <!-- 二級緩存區(qū)域名的前綴 -->  <!--<property name="hibernate.cache.region_prefix">h3test</property>-->  <!-- 高速緩存提供程序  第三方產(chǎn)品-->  <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>  <!-- 強(qiáng)制Hibernate以更人性化的格式將數(shù)據(jù)存入二級緩存 -->  <property name="hibernate.cache.use_structured_entries">true</property>  <!-- Hibernate將收集有助于性能調(diào)節(jié)的統(tǒng)計數(shù)據(jù) -->  <property name="hibernate.generate_statistics">true</property>  

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

<?xml version="1.0" encoding="UTF-8"?>  <ehcache name="h3test">      <!--指定區(qū)域名--><defaultCache  maxElementsInMemory="100"      <!--緩存在內(nèi)存中的最大數(shù)目-->eternal="false"                          <!--緩存是否持久-->timeToIdleSeconds="1200"     <!--當(dāng)緩存條目閑置n秒后銷毀-->timeToLiveSeconds="1200"    <!--當(dāng)緩存條目存活n秒后銷毀-->overflowToDisk="false">    <!--硬盤溢出--></defaultCache>  </ehcache>    

  實(shí)體只讀緩存

只讀緩存 read only,不須要鎖與事務(wù),因?yàn)榫彺孀詳?shù)據(jù)從數(shù)據(jù)庫加載后就不會改變。如果數(shù)據(jù)是只讀的,例如引用數(shù)據(jù),那么總是使用“read-only”策略,因?yàn)樗亲詈唵巍⒆罡咝У牟呗裕彩羌喊踩牟呗浴J切阅艿谝坏牟呗?/p>

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

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

Session session1 = sf.openSession();  Transaction t1 = session1.beginTransaction();  //確保數(shù)據(jù)庫中有標(biāo)識符為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);   //在二級緩存中查找結(jié)果,不會產(chǎn)出sql語句,不操作數(shù)據(jù)庫t2.commit();  session2.close();  sf.close();   

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

  讀寫緩存 read write

  對緩存的更新發(fā)生在數(shù)據(jù)庫事務(wù)完成后。緩存需要支持鎖。在一個事務(wù)中更新數(shù)據(jù)庫,在這個事務(wù)成功完成后更新緩存,并釋放鎖。鎖只是一種特定的緩存值失效表述方式,在它獲得新數(shù)據(jù)庫值前阻止其他事務(wù)讀寫緩存。那些事務(wù)會轉(zhuǎn)而直接讀取數(shù)據(jù)庫

實(shí)體讀/寫緩存

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

  二級緩存測試代碼

Session session1 = sf.openSession();  Transaction t1 = session1.beginTransaction();  //確保數(shù)據(jù)庫中有標(biāo)識符為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);   //該條目已經(jīng)被別的事務(wù)修改了,此時重新查詢一次數(shù)據(jù)庫   t2.commit();  session2.close();  sf.close();   

  允許更新,更新后自動同步到緩存。允許新增,新增記錄后自動同步到緩存。保證read committed隔離級別及可重復(fù)讀隔離級別(通過時間戳實(shí)現(xiàn))整個過程加鎖,如果當(dāng)前事務(wù)的時間戳早于二級緩存中的條目的時間戳,說明該條目已經(jīng)被別的事務(wù)修改了,此時重新查詢一次數(shù)據(jù)庫,否則才使用緩存數(shù)據(jù),因此保證可重復(fù)讀隔離級別

  非嚴(yán)格讀寫緩存 nonstrict read write

  在一個事務(wù)中更新數(shù)據(jù)庫,在這個事務(wù)完成前就清除緩存,為了安全起見,無論事務(wù)成功與否,在事務(wù)完成后再次清除緩存。既不需要支持緩存鎖,也不需要支持事務(wù)。如果是緩存集群,“清除緩存”調(diào)用會讓所有副本都失效,這通常被稱為“拉(pull)”更新策略。如果你的數(shù)據(jù)讀很多或者很少有并發(fā)緩存訪問和更新,那么可以使用“nonstrict-read-write”策略。感謝它的輕量級“拉”更新策略,它通常是性能第二好的策略。

  實(shí)體非嚴(yán)格讀/寫緩存

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

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

  驗(yàn)證結(jié)果

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

  事務(wù)緩存 transactional (一定要在JTA環(huán)境中)

  對緩存和數(shù)據(jù)庫的更新被包裝在同一個JTA事務(wù)中,這樣緩存與數(shù)據(jù)庫總是保持同步的。數(shù)據(jù)庫和緩存都必須支持JTA。除非你真的想將緩存更新和數(shù)據(jù)庫更新放在一個JTA事務(wù)里,否則不要使用“transactional”策略,因?yàn)镴TA需要漫長的兩階段提交處理,這導(dǎo)致它基本是性能最差的策略。

  需要特定緩存的支持和JTA事務(wù)支持,此處不演示。 

  集合緩存

  演示讀/寫緩存示例,和之前實(shí)體緩存測試差不多,其他自測

<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();   //確保數(shù)據(jù)庫中有標(biāo)識符為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();  

  測試結(jié)論:

  和實(shí)體并發(fā)策略有相同含義; 但集合緩存只緩存集合元素的標(biāo)識符,在二級緩存中只存放相應(yīng)實(shí)體的標(biāo)識符,然后再通過標(biāo)識符去二級緩存查找相應(yīng)的實(shí)體最后組合為集合返回。

  查詢緩存 (動手做做)

  1、保證全局配置中有開啟了查詢緩存。

  2、修改FarmModel.hbm.xml,添加如下紅色部分配置,表示實(shí)體緩存并讀/寫

<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();  

  結(jié)論:

  和實(shí)體并發(fā)策略有相同含義; 和集合緩存類似,只緩存集合元素的標(biāo)識符,在二級緩存中只存放相應(yīng)實(shí)體的標(biāo)識符,然后再通過標(biāo)識符 去二級緩存查找相應(yīng)的實(shí)體最后組合為集合返回。

什么時候需要查詢緩存?

大多數(shù)時候無法從結(jié)果集高速緩存獲益。必須知道:每隔多久重復(fù)執(zhí)行同一查詢。對于那些查詢非常多但插入、刪除、更新非常少的應(yīng)用程序來說,查詢緩存可提升性能。但寫入多查詢少的沒有用,總失效。

  管理二級緩存

  對于二級緩存來說,在SessionFactory中定義了許多方法, 清除緩存中實(shí)例、整個類、集合實(shí)例或者整個集合。

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參數(shù)用于控制具體的Session如何與二級緩存進(jìn)行交互。//CacheMode.NORMAL - 從二級緩存中讀、寫數(shù)據(jù)。//CacheMode.GET - 從二級緩存中讀取數(shù)據(jù),僅在數(shù)據(jù)更新時對二級緩存寫數(shù)據(jù)。//CacheMode.PUT - 僅向二級緩存寫數(shù)據(jù),但不從二級緩存中讀數(shù)據(jù)。//CacheMode.REFRESH - 僅向二級緩存寫數(shù)據(jù),但不從二級緩存中讀數(shù)據(jù)。通過 hibernate.cache.use_minimal_puts的設(shè)置,強(qiáng)制二級緩存從數(shù)據(jù)庫中讀取數(shù)據(jù),刷新緩存內(nèi)容

  監(jiān)控二級緩存

  如若需要查看二級緩存或查詢緩存區(qū)域的內(nèi)容,你可以使用統(tǒng)計(Statistics) API。通過sessionFactory.getStatistics();獲取Hibernate統(tǒng)計信息。此時,你必須手工打開統(tǒng)計選項(xiàng)。

 hibernate.generate_statistics true hibernate.cache.use_structured_entries true
五、總結(jié)

  不要想當(dāng)然的以為緩存一定能提高性能,僅僅在你能夠駕馭它并且條件合適的情況下才是這樣的。hibernate的二級緩存限制還是比較多的,不方便用jdbc可能會大大的降低更新性能。在不了解原理的情況下亂用,可能會有1+N的問題。不當(dāng)?shù)氖褂眠€可能導(dǎo)致讀出臟數(shù)據(jù)。

  如果受不了hibernate的諸多限制,那么還是自己在應(yīng)用程序的層面上做緩存吧。

  在越高的層面上做緩存,效果就會越好。就好像盡管磁盤有緩存,數(shù)據(jù)庫還是要實(shí)現(xiàn)自己的緩存,盡管數(shù)據(jù)庫有緩存,咱們的應(yīng)用程序還是要做緩存。因?yàn)榈讓拥木彺嫠⒉恢栏邔右眠@些數(shù)據(jù)干什么,只能做的比較通用,而高層可以有針對性的實(shí)現(xiàn)緩存,所以在更高的級別上做緩存,效果也要好些。

作者:杰瑞教育出處:http://m.survivalescaperooms.com/jerehedu/本文版權(quán)歸煙臺杰瑞教育科技有限公司和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 武宁县| 马龙县| 仪陇县| 康保县| 乐昌市| 桐梓县| 明星| 客服| 木里| 和静县| 思南县| 蒲江县| 两当县| 牡丹江市| 个旧市| 金平| 延寿县| 漳平市| 张北县| 莒南县| 安新县| 蓝山县| 沂源县| 安平县| 合川市| 鞍山市| 合山市| 留坝县| 本溪市| 临城县| 上栗县| 涟水县| 镇雄县| 眉山市| 仙游县| 叶城县| 新民市| 新民市| 河东区| 洪雅县| 蕉岭县|