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