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