Hibernate緩存分兩級緩存java
一級session緩存,就是常說的一級緩存;二級應用緩存(二級緩存);web
一級緩存,一級緩存依賴於session,在一個session中就是一個緩存,當session失效時,緩存消失。spring
public void loadBookAgain(){ Session session = HibernateSessionFactory.getSession(); Book book1 = (Book) session.get(Book.class, 6); Book book2 = (Book) session.get(Book.class, 6); session.close(); // Session session1 = HibernateSessionFactory.getSession(); // Book book2 = (Book) session1.get(Book.class, 6); // session1.close(); }
在一個session裏面查詢兩次相同的book,只會執行一次sql。
但若放在不一樣的session中,將會執行兩次數據庫查詢。 sql
解決不一樣session之間的緩存問題的辦法就是用二級緩存。數據庫
下面這幾種狀況就不適合加載到二級緩存中: 緩存
1.常常被修改的數據 session
2.絕對不容許出現併發訪問的數據 併發
3.與其餘應用共享的數據 ide
下面這己種狀況合適加載到二級緩存中: spa
1.數據更新頻率低
2.容許偶爾出現併發問題的非重要數據
3.不會被併發訪問的數據
4.常量數據
5.不會被第三方修改的數據
配置二級緩存比較簡單,以ehcache爲例:
添加緩存文件ehcache-hibernate-local.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <diskStore path="java.io.tmpdir/hibernate/book" /> <defaultCache maxElementsInMemory="10000" overflowToDisk="true" eternal="false" memoryStoreEvictionPolicy="LRU" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="600" timeToIdleSeconds="3600" timeToLiveSeconds="100000" diskPersistent="false" /> <!-- Special objects setting. --> <cache name="bean.entity.Book" maxElementsInMemory="500" overflowToDisk="true" eternal="true"> </cache> </ehcache>
maxElementsInMemory爲緩存對象的最大數目,
eternal設置是否永遠不過時,
timeToIdleSeconds對象處於空閒狀態的最多秒數,
timeToLiveSeconds對象處於緩存狀態的最多秒數 。
在實體bean的hbm.xml文件中加上緩存配置:
<!-- 設置該持久化類的二級緩存併發訪問策略 read-only read-write nonstrict-read-write transactional--> <cache usage="read-write" />
如今大部分的hibernate應用再也不寫實體映射配置文件,那麼就在實體bean中加上
//默認的緩存策略.
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
在hibernate定義sessionFactory中加上查詢緩存配置:
<!-- 設置二級緩存插件EHCache的Provider類--> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <!-- 啓動"查詢緩存" --> <property name="hibernate.cache.use_query_cache">true</property> <property name="hibernate.cache.provider_configuration_file_resource_path"> /ehcache-hibernate-local.xml </property>
若是項目試用了spring,那麼相應配置爲:
<property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> <prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache-hibernate-local.xml</prop> </props> </property>
兩種配置基本一致。
這個時候在按實體的ID來查詢的時候即便不在一個session中,hibernate也只是執行一次sql。
查詢緩存作到如今,有一點效果,但基本仍是個擺設。
public void listBookTwice(){ String hql = "from Book"; Session session = HibernateSessionFactory.getSession(); Query q = session.createQuery(hql); List<Book> list1 = q.list(); List<Book> list2 = q.list(); session.close();
同一個query。list了兩次,按照以前的效果,應該是執行一個sql。事實是,他要去查兩次,
在一個query尚且如此,兩個不用說,確定也是沒有用到緩存了。
難道緩存失效了?呵呵,實際上是由於咱們雖然配置了緩存,可是在query級卻沒有設置緩存,若是須要query緩存,
則須要手工寫入:
q.setCacheable(true);來激活查詢緩存。
修改代碼以下:
public void listBookTwice(){ String hql = "from Book"; Session session = HibernateSessionFactory.getSession(); Query q = session.createQuery(hql); q.setCacheable(true); List<Book> list1 = q.list(); for(Book b : list1){ System.out.println(b.getBname()+"--------->list1"); } // List<Book> list2 = q.list(); session.close(); Session session2 = HibernateSessionFactory.getSession(); Query q2 = session2.createQuery(hql); q2.setCacheable(true); List<Book> list2 = q2.list(); for(Book b : list2){ System.out.println(b.getBname()+"--------->list2"); } session2.close(); }
在兩個session立分別list查詢,ok,只輸出一條sql。說明二級緩存在list查詢的時候也起做用了。
對於查詢緩存來講,緩存的key是根據hql生成的sql,再加上參數,分頁等信息(能夠經過日誌輸出看到,不過它的輸出不是很可讀,最好改一下它的代碼)。
好比hql:
from Cat c where c.name like ?
生成大體以下的sql:
select * from cat c where c.name like ?
參數是"tiger%",那麼查詢緩存的key*大約*是這樣的字符串(我是憑記憶寫的,並不精確,不過看了也該明白了):
select * from cat c where c.name like ? , parameter:tiger%
這樣,保證了一樣的查詢、一樣的參數等條件下具備同樣的key。
如今說說緩存的value,若是是list方式的話,value在這裏並非整個結果集,而是查詢出來的這一串ID。
也就是說,不論是list方法仍是iterate方法,第一次查詢的時候,它們的查詢方式很它們平時的方式是同樣的,
list執行一條sql,iterate執行1+N條,多出來的行爲是它們填充了緩存。可是到一樣條件第二次查詢的時候,就都和iterate的行爲同樣了, 根據緩存的key去緩存裏面查到了value,value是一串id,而後在到class的緩存裏面去一個一個的load出來。這樣作是爲了節約內存。
能夠看出來,查詢緩存須要打開相關類的class緩存。list和iterate方法第一次執行的時候,都是既填充查詢緩存又填充class緩存的。
這裏還有一個很容易被忽視的重要問題,即打開查詢緩存之後,即便是list方法也可能遇到1+N的問題!相同條件第一次list的時候, 由於查詢緩存中找不到,無論class緩存是否存在數據,老是發送一條sql語句到數據庫獲取所有數據,而後填充查詢緩存和class緩存。
可是第二次執行的時候,問題就來了,若是你的class緩存的超時時間比較短,如今class緩存都超時了,可是查詢緩存還在,
那麼list方法在獲取id串之後,將會一個一個去數據庫load!所以,class緩存的超時時間必定不能短於查詢緩存設置的超時時間
!若是還設置了發呆時間的話,保證class緩存的發呆時間也大於查詢的緩存的生存時間。這裏還有其餘狀況,好比class緩存被程序強制evict了,
這種狀況就請本身注意了。
另外,若是hql查詢包含select字句,那麼查詢緩存裏面的value就是整個結果集了。
能夠理解爲hibernate緩存了每次查詢的hql語句做爲緩存map的key,將對應對象的id做爲value緩存,每次遇到相同的hql,就將id取出來, 若是在緩存裏面有對象,就從緩存取,沒有的話就去數據庫load
緩存何時更新呢?
public void update(){ Session session = HibernateSessionFactory.getSession(); Transaction tran = session.beginTransaction(); tran.begin(); Book b = (Book) session.get(Book.class, 7); b.setIsbn("567890"); session.saveOrUpdate(b); tran.commit(); session.close(); } public void listBookTwice(){ String hql = "from Book"; Session session = HibernateSessionFactory.getSession(); Query q = session.createQuery(hql); q.setCacheable(true); List<Book> list1 = q.list(); for(Book b : list1){ System.out.println(b.getBname()+"----"+b.getIsbn()+"--------->list1"); } session.close(); } public void listBookAgain(){ String hql = "from Book"; Session session2 = HibernateSessionFactory.getSession(); Query q2 = session2.createQuery(hql); q2.setCacheable(true); List<Book> list2 = q2.list(); for(Book b : list2){ System.out.println(b.getBname()+"----"+b.getIsbn()+"--------->list3"); } session2.close(); } public static void main(String[] args) { BookDao dao = new BookDao(); dao.listBookTwice(); dao.update(); dao.listBookAgain(); }
先list一次,之間更新一個book,第二次list,輸出:
Hibernate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0_ from hibernate_test.dbo.book book0_ book1250672666171----123456--------->list1 book1250672666203----123456--------->list1 book1250672666203----123456--------->list1 book1250672666203----123456--------->list1 Hibernate: update hibernate_test.dbo.book set bname=?, isbn=?, price=? where id=? Hibernate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0_ from hibernate_test.dbo.book book0_ book1250672666171----123456--------->list3 book1250672666203----567890--------->list3 book1250672666203----123456--------->list3 book1250672666203----123456--------->list3 book1250672666203----123456--------->list3可見,當數據庫有更新的時候,緩存就失效了。