1、查詢緩存
? 首先需要配置hibernate.cache.use_query_cache=true
如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了
?1?<cache?name="net.sf.hibernate.cache.StandardQueryCache"
?2?
?3?maxElementsInMemory="50"?eternal="false"?timeToIdleSeconds="3600"
?4?
?5?timeToLiveSeconds="7200"?overflowToDisk="true"/>
?6?
?7?<cache?name="net.sf.hibernate.cache.UpdateTimestampsCache"
?8?
?9?maxElementsInMemory="5000"?eternal="true"?overflowToDisk="true"/>
10?
11?
? 然后
query.setCacheable(true);//激活查詢緩存
query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可選
第二行指定要使用的cacheRegion是myCacheRegion,即你可以給每個查詢緩存做一個單獨的配置,
使用setCacheRegion來做這個指定,需要在ehcache.xml里面配置它:
1
<
cache?
name
="myCacheRegion"
?maxElementsInMemory
="10"
?eternal
="false"
2
3
timeToIdleSeconds
="3600"
?timeToLiveSeconds
="7200"
?overflowToDisk
="true"
?
/>
4
5
如果省略第二行,不設(shè)置cacheRegion的話,那么會使用上面提到的標(biāo)準(zhǔn)查詢緩存的配置,也就是
net.sf.hibernate.cache.StandardQueryCache
? 問題:當(dāng)hibernate更新數(shù)據(jù)庫的時候,它怎么知道更新哪些查詢緩存呢?
??? hibernate在一個地方維護每個表的最后更新時間,其實也就是放在上面
net.sf.hibernate.cache.UpdateTimestampsCache所指定的緩存配置里面。
當(dāng)通過hibernate更新的時候,hibernate會知道這次更新影響了哪些表。然后它更新這些表的最后更新時間。每個緩存都有一個生成時間和這個緩存所查詢的表,當(dāng)hibernate查詢一個緩存是否存在的時候,如果緩存存在,它還要取出緩存的生成時間和這個緩存所查詢的表,然后去查找這些表的最后更新時間,如果有一個表在生成時間后更新過了,那么這個緩存是無效的。可以看出,只要更新過一個表,那么凡是涉及到這個表的查詢緩存就失效了,因此查詢緩存的命中率可能會比較低。
? 注意:
??? 放入緩存中的key是查詢的語句,value是查詢之后得到的結(jié)果集的id列表。
表面看來這樣的方案似乎能解決hql利用緩存的問題,但是需要注意的是,構(gòu)成key的是:
hql生成的sql、sql的參數(shù)、排序、分頁信息等。也就是說如果你的hql有小小的差異,
比如第一條hql取1-50條數(shù)據(jù),第二條hql取20-60條數(shù)據(jù),那么hibernate會認(rèn)為這是兩個
完全不同的key,無法重復(fù)利用緩存。因此利用率也不高。
?? 查詢緩存必須配合二級緩存一起使用,否則極易出現(xiàn)1+N的情況
?? 如果不設(shè)置"查詢緩存",那么hibernate只會緩存使用load()方法獲得的單個持久化對象,
如果想緩存使用findall()、list()、Iterator()、createCriteria()、createQuery()等方法
獲得的數(shù)據(jù)結(jié)果集的話,就需要設(shè)置hibernate.cache.use_query_cache true才行,如果需要"查詢緩存",需要在使用Query或Criteria()時設(shè)置其setCacheable(true);屬性。?
2、Collection緩存
? 需要在hbm的collection里面設(shè)置
<
cache?
usage
="read-write"
/>
假如class是Cat,collection叫children,那么ehcache里面配置
1
<
cache?
name
="com.xxx.pojo.Cat.children"
2
3
maxElementsInMemory
="20"
?eternal
="false"
?timeToIdleSeconds
="3600"
4
5
timeToLiveSeconds
="7200"
6
7
overflowToDisk
="true"
?
/>
8
Collection的緩存和前面查詢緩存的list一樣,也是只保持一串id,但它不會因為這個表更新過就
失效,一個collection緩存僅在這個collection里面的元素有增刪時才失效。
這樣有一個問題,如果你的collection是根據(jù)某個字段排序的,當(dāng)其中一個元素更新了該字段時,
導(dǎo)致順序改變時,collection緩存里面的順序沒有做更新?
3、Class緩存(也稱二級緩存)
?? 可由Hibernate提供的二級緩存實現(xiàn),它存在于SessionFactory級別,
緩存結(jié)構(gòu)可以看作是一個hash table,key是數(shù)據(jù)庫記錄的id,value是id對應(yīng)的pojo對象。
當(dāng)用戶根據(jù)id查詢對象的時候(load、iterator方法),會首先在緩存中查找,
如果沒有找到再發(fā)起數(shù)據(jù)庫查詢。但是如果使用hql發(fā)起查詢(find, query方法)
則不會利用二級緩存,而是直接從數(shù)據(jù)庫獲得數(shù)據(jù),但是它會把得到的數(shù)據(jù)放到二級緩存?zhèn)溆谩R簿褪钦f,基于hql的查詢,對二級緩存是只寫不讀的。?
在hibernate.cfg.xml配置:
<
property?
name
="hibernate.cache.provider_class"
>
???????org.hibernate.cache.EhCacheProvider

</
property
>
hibernate3支持
Hashtable Cache(org.hibernate.cache.HashtableCacheProvider)
EhCache(org.hibernate.cache.EhCacheProvider)
OSCache(org.hibernate.cache.OSCacheProvider)
SwarmCache(org.hibernate.cache.SwarmCacheProvider)
JBossCache(org.hibernate.cache.TreeCacheProvider)
在相應(yīng)的POJO配置文件中增加:
<!--
指定緩存同步策略:
read-write???????????????????????????讀寫型
nonstrict-read-write?????????????非嚴(yán)格讀寫型
read-only?????????????????????????? ?只讀型
transactional??????????????????????? 事務(wù)型
-->
<cache usage="read-write"/>?
Cache |
read-only |
nonstrict-read-write |
read-write |
transactional |
EhCache |
Yes |
Yes |
Yes |
No |
OSCache |
Yes |
Yes |
Yes |
No |
SwarmCache |
Yes |
Yes |
No |
No |
TreeCache |
Yes |
No |
No |
Yes |
?????對于一條記錄,也就是一個PO來說,是根據(jù)ID來找的,緩存的key就是ID,value是POJO。無論
list,load還是iterate,只要讀出一個對象,都會填充緩存。但是list不會使用緩存,而iterate
會先取數(shù)據(jù)庫select id出來,然后一個id一個id的load,如果在緩存里面有,就從緩存取,沒有
的話就去數(shù)據(jù)庫load。
?? 當(dāng)某個ID通過hibernate修改時,hibernate會知道,于是移除緩存。
這樣大家可能會想,同樣的查詢條件,第一次先list,第二次再iterate,就可以使用到緩存了。
實際上這是很難的,因為你無法判斷什么時候是第一次,而且每次查詢的條件通常是不一樣的,假
如數(shù)據(jù)庫里面有100條記錄,id從1到100,第一次list的時候出了前50個id,第二次iterate的時候
卻查詢到30至70號id,那么30-50是從緩存里面取的,51到70是從數(shù)據(jù)庫取的,共發(fā)送1+20條sql。
所以我一直認(rèn)為iterate沒有什么用,總是會有1+N的問題。 如果想要對list或者iterate查詢的結(jié)
果緩存,就要用到查詢緩存了。?
? 注意:
?? 對于one-to-many的關(guān)聯(lián)關(guān)系,如果對one應(yīng)用緩存,則應(yīng)同時對many應(yīng)用。
?? 二級緩存的失效機制由hibernate控制,當(dāng)某條數(shù)據(jù)被修改之后,hibernate會根據(jù)它的id去做
緩存失效操作。基于此機制,如果數(shù)據(jù)表不是被hibernate獨占,那么二級緩存無法得到有效控制
。
?? hibernate 3.0在做批量修改、批量更新的時候,是不會同步更新二級緩存的
?? 查詢緩存和二級緩存是有關(guān)聯(lián)關(guān)系的,他們不是完全獨立的兩套東西。假如一個查詢條件hql_1
,第一次被執(zhí)行的時候,它會從數(shù)據(jù)庫取得數(shù)據(jù),然后把查詢條件作為key,把返回數(shù)據(jù)的所有id
列表作為value(請注意僅僅是id)放到查詢緩存中,同時整個結(jié)果集放到二級緩存,key是id,
value是pojo對象。當(dāng)你再次執(zhí)行hql_1,它會從緩存中得到id列表,然后根據(jù)這些列表一個一個的
到二級緩存里面去找pojo對象,如果找不到就向數(shù)據(jù)庫發(fā)起查詢。也就是說,如果二級緩存配置了
超時時間,就有可能出現(xiàn)查詢緩存命中了,獲得了id列表,但是class里面相應(yīng)的pojo已經(jīng)因為超
時被失效,hibernate就會根據(jù)id清單,一個一個的去向數(shù)據(jù)庫查詢,有多少個id,就執(zhí)行多少個
sql。該情況將導(dǎo)致性能下降嚴(yán)重。
?? 查詢緩存的失效機制也由hibernate控制,數(shù)據(jù)進入緩存時會有一個timestamp,它和數(shù)據(jù)表的
timestamp對應(yīng)。當(dāng)hibernate環(huán)境內(nèi)發(fā)生save、update等操作時,會更新被操作數(shù)據(jù)表的
timestamp。用戶在獲取緩存的時候,一旦命中就會檢查它的timestamp是否和數(shù)據(jù)表的timestamp
匹配,如果不,緩存會被失效。因此查詢緩存的失效控制是以數(shù)據(jù)表為粒度的,只要數(shù)據(jù)表中任何
一條記錄發(fā)生一點修改,整個表相關(guān)的所有查詢緩存就都無效了。因此查詢緩存的命中率可能會很
低。?
4、對方法的緩存
? 對于調(diào)用頻率較高的查詢類方法,我們希望緩存方法結(jié)果
在spring中可利用攔截器(Interceptor)實現(xiàn),可能如下面的配置:
?1
<
beans
>
?2
?3
????
<
bean?
id
="cacheInterceptor"
?4
?5
class
="org.springframework.aop.interceptor.cache.OSCacheInterceptor"
>
?6
?7
???????
<!
-?默認(rèn)刷新時間(秒)-
>
?8
?9
???????
<
property?
name
="defaultRefreshPeriod"
>
10
11
???????????
<
value
>
60
</
value
>
12
13
???????
</
property
>
14
15
???????
<
property?
name
="identifiers"
>
16
17
???????????
<
props
>
18
19
??????????????
<
prop?
key
="java.util.Map"
>
toString
</
prop
>
20
21
???????????
</
props
>
22
23
???????
</
property
>
24
25
????
</
bean
>
26
27
????
<!
-?正則表達式匹配?-
>
28
29
????
<
bean?
id
="searcherAdvisor"
30
31
class
="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
>
32
33
???????
<
property?
name
="advice"
>
34
35
???????????
<
ref?
local
="cacheInterceptor"
?
/>
36
37
???????
</
property
>
38
39
???????
<
property?
name
="patterns"
>
40
41
???????????
<
list
>
42
43
??????????????
<
value
>
.get.
</
value
>
44
45
???????????
</
list
>
46
47
???????
</
property
>
48
49
????
</
bean
>
50
51
????
<!
-?自動代理?-
>
52
53
????
<
bean?
id
="proxyCreator1"
54
55
class
="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
>
56
57
???????
<
property?
name
="beanNames"
>
58
59
??????????
<!--
?需截獲方法的bean列表?
-->
60
61
???????????
<
list
>
62
63
??????????????
<
value
>
commonSelect
</
value
>
64
65
???????????
</
list
>
66
67
???????
</
property
>
68
69
???????
<
property?
name
="interceptorNames"
>
70
71
??????????
<!--
?截獲器列表?
-->
72
73
???????????
<
list
>
74
75
??????????????
<
value
>
searcherAdvisor
</
value
>
76
77
???????????
</
list
>
78
79
???????
</
property
>
80
81
????
</
bean
>
82
83
<
beans
>
84
85
?這里通過自動代理創(chuàng)建bean,并指定作用其上的interceptor列表,在commonSelect存在searchAdvisor,
而searchAdvisor對匹配*get*模式的方法調(diào)用將通過cacheInterceptor進行處理。