對(duì)于查詢緩存來(lái)說(shuō),緩存的key是根據(jù)hql生成的sql,再加上參數(shù),分頁(yè)等信息(可以通過(guò)日志輸出看到,不過(guò)它的輸出不是很可讀,最好改一下它的代碼)。 比如hql: from Cat c where c.name like ? 生成大致如下的sql: select * from cat c where c.name like ? 參數(shù)是"tiger%",那么查詢緩存的key*大約*是這樣的字符串(我是憑記憶寫的,并不精確,不過(guò)看了也該明白了): select * from cat c where c.name like ? , parameter:tiger% 這樣,保證了同樣的查詢、同樣的參數(shù)等條件下具有一樣的key。 現(xiàn)在說(shuō)說(shuō)緩存的value,如果是list方式的話,value在這里并不是整個(gè)結(jié)果集,而是查詢出來(lái)的這一串ID。也就是說(shuō),不管是list方法還是iterate方法,第一次查詢的時(shí)候,它們的查詢方式很它們平時(shí)的方式是一樣的,list執(zhí)行一條sql,iterate執(zhí)行1+N條,多出來(lái)的行為是它們填充了緩存。但是到同樣條件第二次查詢的時(shí)候,就都和iterate的行為一樣了,根據(jù)緩存的key去緩存里面查到了value,value是一串id,然后在到class的緩存里面去一個(gè)一個(gè)的load出來(lái)。這樣做是為了節(jié)約內(nèi)存。 可以看出來(lái),查詢緩存需要打開相關(guān)類的class緩存。list和iterate方法第一次執(zhí)行的時(shí)候,都是既填充查詢緩存又填充class緩存的。 這里還有一個(gè)很容易被忽視的重要問(wèn)題,即打開查詢緩存以后,即使是list方法也可能遇到1+N的問(wèn)題!相同條件第一次list的時(shí)候,因?yàn)椴樵兙彺嬷姓也坏?,不管class緩存是否存在數(shù)據(jù),總是發(fā)送一條sql語(yǔ)句到數(shù)據(jù)庫(kù)獲取全部數(shù)據(jù),然后填充查詢緩存和class緩存。但是第二次執(zhí)行的時(shí)候,問(wèn)題就來(lái)了,如果你的class緩存的超時(shí)時(shí)間比較短,現(xiàn)在class緩存都超時(shí)了,但是查詢緩存還在,那么list方法在獲取id串以后,將會(huì)一個(gè)一個(gè)去數(shù)據(jù)庫(kù)load!因此,class緩存的超時(shí)時(shí)間一定不能短于查詢緩存設(shè)置的超時(shí)時(shí)間!如果還設(shè)置了發(fā)呆時(shí)間的話,保證class緩存的發(fā)呆時(shí)間也大于查詢的緩存的生存時(shí)間。這里還有其他情況,比如class緩存被程序強(qiáng)制evict了,這種情況就請(qǐng)自己注意了。
使用二級(jí)緩存的前置條件 你的hibernate程序?qū)?shù)據(jù)庫(kù)有獨(dú)占的寫訪問(wèn)權(quán),其他的進(jìn)程更新了數(shù)據(jù)庫(kù),hibernate是不可能知道的。你操作數(shù)據(jù)庫(kù)必需直接通過(guò)hibernate,如果你調(diào)用存儲(chǔ)過(guò)程,或者自己使用jdbc更新數(shù)據(jù)庫(kù),hibernate也是不知道的。hibernate3.0的大批量更新和刪除是不更新二級(jí)緩存的,但是據(jù)說(shuō)3.1已經(jīng)解決了這個(gè)問(wèn)題。 這個(gè)限制相當(dāng)?shù)募郑袝r(shí)候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來(lái)優(yōu)化,很郁悶吧。 SessionFactory也提供了移除緩存的方法,你一定要自己寫一些JDBC的話,可以調(diào)用這些方法移除緩存,這些方法是: void evict(Class persistentClass) Evict all entries from the second-level cache. void evict(Class persistentClass, Serializable id) Evict an entry from the second-level cache. void evictCollection(String roleName) Evict all entries from the second-level cache. void evictCollection(String roleName, Serializable id) Evict an entry from the second-level cache. void evictQueries() Evict any query result sets cached in the default query cache region. void evictQueries(String cacheRegion) Evict any query result sets cached in the named query cache region. 不過(guò)我不建議這樣做,因?yàn)檫@樣很難維護(hù)。比如你現(xiàn)在用JDBC批量更新了某個(gè)表,有3個(gè)查詢緩存會(huì)用到這個(gè)表,用evictQueries(String cacheRegion)移除了3個(gè)查詢緩存,然后用evict(Class persistentClass)移除了class緩存,看上去好像完整了。不過(guò)哪天你添加了一個(gè)相關(guān)查詢緩存,可能會(huì)忘記更新這里的移除代碼。如果你的jdbc代碼到處都是,在你添加一個(gè)查詢緩存的時(shí)候,還知道其他什么地方也要做相應(yīng)的改動(dòng)嗎?