在hibernate中我們最常用的有三類緩存,分別為一級緩存、二級緩存和查詢緩存,下面我們對這三個緩存在項(xiàng)目中的使用以及優(yōu)缺點(diǎn)分析一下。 緩存它的作用在于提高性能系統(tǒng)性能,介于應(yīng)用系統(tǒng)與數(shù)據(jù)庫之間而存在于內(nèi)存或磁盤上的數(shù)據(jù)。 我們編程的模式一般是這樣的page-->filter-->action-->server-->dao-->db,可以在這一個請求過程中的任何一點(diǎn)加入緩存,上一篇介紹的是在server層加緩存:service利用aop加緩存。 為頁面增加緩存:為頁面加緩存 首先,來看一下一級緩存它默認(rèn)開啟且很常用。 一級緩存
當(dāng)session關(guān)閉后緩存中的對象會丟失,也就是說兩個不同的session中的緩存數(shù)據(jù)都是不一樣的,緩存數(shù)據(jù)不能夠跨session訪問。
緩存數(shù)據(jù)的數(shù)據(jù)類型 在一級緩存中緩存的是實(shí)體對象,在使用查詢方法get() 、load() 、iterate()三個方法查詢時都會先查詢session緩存,如果有對象則從緩存里面取出來,如果緩存中沒有再去數(shù)據(jù)庫里面查詢。load()測試:測試注意一定要在同一個事務(wù)里面,當(dāng)我在Spring管理的session測試時調(diào)用兩次load()總是查詢兩次發(fā)出兩條SQL語句,還以為session級緩存沒有起作用,原來是因?yàn)閔ibernate集成spring之后事務(wù)、session都由spring管理,每次調(diào)用前后事務(wù)一級session都自動打開和關(guān)閉,自己控制不了中間過程,于是將spring去掉拿到hibernate原session,再手動開發(fā)關(guān)閉事務(wù)這樣做可以保證在同一個session、同一個事務(wù)里面操作方法,確實(shí)是發(fā)了一條SQL語句,看下面代碼: load()、get()方法:
結(jié)果
結(jié)果不僅發(fā)送了一條語句而且兩個對象打印出來也是一樣的。 load()、get()第一次查詢時會發(fā)出sql語句,從數(shù)據(jù)庫表里面查詢;第二次查詢時會先去緩存里面查找,如果沒有發(fā)生更新修改操作,那么將從緩存中讀取數(shù)據(jù),否則查詢數(shù)據(jù)庫。 save方法
save也支持緩存,當(dāng)執(zhí)行save方法時首先往session緩存里面添加一條數(shù)據(jù),等事務(wù)提交或者緩存刷新時才往數(shù)據(jù)庫里面更新,從上面執(zhí)行過程可以看出只發(fā)出了一條插入語句沒有發(fā)查詢語句,因?yàn)榈诙问菑木彺嬷胁樵兂鰜淼摹?br> PS:save之后執(zhí)行g(shù)et或者load需要知道對象的ID,此時save方法執(zhí)行后雖然數(shù)據(jù)庫里沒有數(shù)據(jù),但是對象的ID已經(jīng)生成可以通過這個ID查詢對象。 批量插入數(shù)據(jù) 在批量插入數(shù)據(jù)的時候采取每次插入一部分?jǐn)?shù)據(jù),如下,每次插入20條數(shù)據(jù)不需要一條一條插入。
每次20條數(shù)據(jù)清理一下緩存,每次清理緩存調(diào)用session.flush()方法會發(fā)出20條insert語句,但是數(shù)據(jù)庫里面還沒有數(shù)據(jù)等所有數(shù)據(jù)都發(fā)出insert語句統(tǒng)一提交事務(wù),事務(wù)同session是一個等級的因此需統(tǒng)一控制事務(wù)。
hibernate N+1問題 Hibernate 中常會用到 set 等集合表示 1 對多的關(guān)系,在我們做的這個鐵科院項(xiàng)目中,在獲取實(shí)體的時候就能根據(jù)關(guān)系將關(guān)聯(lián)的對象或者對象集合取出,還可以設(shè)定 cacade 進(jìn)行關(guān)聯(lián)更新和刪除。這不得不說 hibernate 的 orm 做得很好,很貼近 oo 的使用習(xí)慣了。
OpenSessionInview問題 這個問題出現(xiàn)是由于load()懶加載導(dǎo)致的,第一次查詢數(shù)據(jù)時使用了懶加載至查詢出來數(shù)據(jù)的ID,當(dāng)使用數(shù)據(jù)的時候還需要去數(shù)據(jù)庫里面查詢但是此時數(shù)據(jù)庫的session已經(jīng)關(guān)閉,解決此問題兩種思路一種是不使用懶加載;其二是在web層開發(fā)關(guān)閉session,延長session的生命周期。
二級緩存
二級緩存也稱為進(jìn)程級緩存或sessionFactory緩存,也可以叫做集群范圍內(nèi)的緩存,需要第三方來實(shí)現(xiàn),hibernate默認(rèn)的二級緩存插件為ehcache這個緩存,由于二級緩存是進(jìn)程級的可能出現(xiàn)多線程并發(fā)問題,需要設(shè)置緩存的并發(fā)策略。 hibernate二級緩存需要第三方插件支持,hibernate默認(rèn)支持為ehcache關(guān)于配置請參考:Spring AOP +EHcache為Service層方法增加緩存
開啟二級緩存后對方法的影響 get()/load() 對于這兩個方法沒啥影響,第一次從數(shù)據(jù)庫里面查詢,第二次先判斷緩存里面有沒有數(shù)據(jù)如果沒有再去數(shù)據(jù)庫里面查詢。
查詢緩存
查詢緩存是針對普通屬性結(jié)果集的緩存,不緩存實(shí)體對象,當(dāng)和查詢緩存關(guān)聯(lián)的表發(fā)生修改的時候,查詢緩存生命周期結(jié)束,里面的數(shù)據(jù)也隨即被清空了。
代碼中,加上一句話
一級、二級、查詢之間的關(guān)系
開啟二級緩存時,如果兩個session先后執(zhí)行l(wèi)oad或者get方法,只執(zhí)行一條語句第二次會從緩存中查找,先從一級緩存中查詢,如果沒有再去二級緩存中查找。 一級緩存同二級緩存交互
禁止一級緩存與二級緩存交互,如下設(shè)置 session.setCacheMode(CacheMode.IGNORE);打開一個session執(zhí)行查詢,它會先將查詢結(jié)果保存到一級緩存,待session關(guān)閉后,一級緩存中數(shù)據(jù)清空,由于禁止了一級緩存同二級緩存數(shù)據(jù)交互,因此,一級緩存關(guān)閉后不會將結(jié)構(gòu)保存到二級緩存,打開第二個session后,后再發(fā)送一條查詢語句,因此二級緩存中沒有數(shù)據(jù)。 查詢緩存與二級緩存
如果兩次執(zhí)行query.list(),第一次發(fā)送查詢語句會將結(jié)果對象的id保存到查詢緩存中,第二次會先從查詢緩存中取出ID,根據(jù)id先去一級緩存查找,再二級緩存,如果沒有找到會去數(shù)據(jù)庫中查找,一級緩存同session沒有關(guān)系,只和表有關(guān)系。
開啟查詢,開啟二級緩存 兩次執(zhí)行query.list(),第一次發(fā)送查詢語句將結(jié)果
總結(jié): 緩存在一個項(xiàng)目中對于提高系統(tǒng)性能很重要,除了ehcache之外還有memcache、redis等緩存產(chǎn)品目前都很常用,redis具有豐富的數(shù)據(jù)類型以及單線程高效能訪問效率,memcache雖然是多線程但效率還是沒有redis高。 這些緩存產(chǎn)品都可以實(shí)現(xiàn)分布式緩存,ehcache+rmi可以分布式緩存同步;memcache+redis都支持分布式,redis還提供了高可用性的解決方案:主從復(fù)制幾個服務(wù)器直接愛你可以切換。
|
|