Hibernate的緩存解讀
緩存是介于物理數據源與應用程序之間,是對數據庫中的數據復制一份臨時放在內存中的容器,其作用是為了減少應用程序對物理數據源訪問的次數,從而提高了應用的運行性能。Hibernate在進行讀取數據的時候,根據緩存機制在相應的緩存中查詢,如果在緩存中找到了需要的數據(我們把這稱做“緩存命中"),則就直接把命中的數據作為結果加以利用,避免了大量發送SQL語句到數據庫查詢的性能損耗。
Hibernate緩存分類:
一、Session緩存(又稱作事務緩存):Hibernate內置的,不能卸除。
緩存范圍:緩存只能被當前Session對象訪問。緩存的生命周期依賴于Session的生命周期,當Session被關閉后,緩存也就結束生命周期。
二、SessionFactory緩存(又稱作應用緩存):使用第三方插件,可插拔。
緩存范圍:緩存被應用范圍內的所有session共享。這些session有可能是并發訪問緩存,因此必須對緩存進行更新。緩存的生命周期依賴于應用的生命周期,應用結束時,緩存也就結束了生命周期,二級緩存存在于應用程序范圍。
Hibernate一些與一級緩存相關的操作(時間點):
數據放入緩存:
1. save()。當session對象調用save()方法保存一個對象后,該對象會被放入到session的緩存中。
2. get()和load()。當session對象調用get()或load()方法從數據庫取出一個對象后,該對象也會被放入到session的緩存中。
3. 使用HQL和QBC等從數據庫中查詢數據。
例如:數據庫有一張表如下:
使用get()或load()證明緩存的存在:
- public class Client
- {
- public static void main(String[] args)
- {
- Session session = HibernateUtil.getSessionFactory().openSession();
- Transaction tx = null;
- try
- {
- /*開啟一個事務*/
- tx = session.beginTransaction();
- /*從數據庫中獲取id="402881e534fa5a440134fa5a45340002"的Customer對象*/
- Customer customer1 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
- System.out.println("customer.getUsername is"+customer1.getUsername());
- /*事務提交*/
- tx.commit();
- System.out.println("-------------------------------------");
- /*開啟一個新事務*/
- tx = session.beginTransaction();
- /*從數據庫中獲取id="402881e534fa5a440134fa5a45340002"的Customer對象*/
- Customer customer2 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
- System.out.println("customer2.getUsername is"+customer2.getUsername());
- /*事務提交*/
- tx.commit();
- System.out.println("-------------------------------------");
- /*比較兩個get()方法獲取的對象是否是同一個對象*/
- System.out.println("customer1 == customer2 result is "+(customer1==customer2));
- }
- catch (Exception e)
- {
- if(tx!=null)
- {
- tx.rollback();
- }
- }
- finally
- {
- session.close();
- }
- }
- }
程序控制臺輸出結果:
- Hibernate:
- select
- customer0_.id as id0_0_,
- customer0_.username as username0_0_,
- customer0_.balance as balance0_0_
- from
- customer customer0_
- where
- customer0_.id=?
- customer.getUsername islisi
- -------------------------------------
- customer2.getUsername islisi
- -------------------------------------
- customer1 == customer2 result is true
輸出結果中只包含了一條SELECT SQL語句,而且customer1 == customer2 result is true說明兩個取出來的對象是同一個對象。其原理是:***次調用get()方法, Hibernate先檢索緩存中是否有該查找對象,發現沒有,Hibernate發送SELECT語句到數據庫中取出相應的對象,然后將該對象放入緩存中,以便下次使用,第二次調用get()方法,Hibernate先檢索緩存中是否有該查找對象,發現正好有該查找對象,就從緩存中取出來,不再去數據庫中檢索。
數據從緩存中清除:
1. evit()將指定的持久化對象從緩存中清除,釋放對象所占用的內存資源,指定對象從持久化狀態變為脫管狀態,從而成為游離對象。
2. clear()將緩存中的所有持久化對象清除,釋放其占用的內存資源。
其他緩存操作:
1. contains()判斷指定的對象是否存在于緩存中。
2. flush()刷新緩存區的內容,使之與數據庫數據保持同步。
Hibernate使用二級緩存
適合存放到第二級緩存中的數據:
1. 很少被修改的數據。
2. 不是很重要的數據,允許出現偶爾并發的數據。
3. 不會被并發訪問的數據。
4. 參考數據,指的是供應用參考的常量數據,它的實例數目有限,它的實例會被許多其他類的實例引用,實例極少或者從來不會被修改。
不適合存放到第二級緩存的數據:
1. 經常被修改的數據。
2. 財務數據,絕對不允許出現并發。
3. 與其他應用共享的數據。
Hibernate如何將數據庫中的數據放入到二級緩存中?注意,你可以把緩存看做是一個Map對象,它的Key用于存儲對象OID,Value用于存儲POJO。首先,當我們使用Hibernate從數據庫中查詢出數據,獲取檢索的數據后,Hibernate將檢索出來的對象的OID放入緩存中key中,然后將具體的POJO放入value中,等待下一次再次向數據查詢數據時,Hibernate根據你提供的OID先檢索一級緩存,若沒有且配置了二級緩存,則檢索二級緩存,如果還沒有則才向數據庫發送SQL語句,然后將查詢出來的對象放入緩存中。
為Hibernate配置二級緩存:
在主配置文件中hibernate.cfg.xml
- <property name="hibernate.cache.use_second_level_cache">true</property>
- <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
配置ehcache.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache>
- <!--
- 緩存到硬盤的路徑
- -->
- <diskStore path="d:/ehcache"></diskStore>
- <!--
- 默認設置
- maxElementsInMemory : 在內存中***緩存的對象數量。
- eternal : 緩存的對象是否永遠不變。
- timeToIdleSeconds :可以操作對象的時間。
- timeToLiveSeconds :緩存中對象的生命周期,時間到后查詢數據會從數據庫中讀取。
- overflowToDisk :內存滿了,是否要緩存到硬盤。
- -->
- <defaultCache maxElementsInMemory="200" eternal="false"
- timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>
- <!--
- 指定緩存的對象。
- 下面出現的的屬性覆蓋上面出現的,沒出現的繼承上面的。
- -->
- <cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false"
- timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>
- </ehcache>
在需要被緩存的對象中hbm文件中的<class>標簽下添加一個<cache>子標簽
- <hibernate-mapping>
- <class name="com.suxiaolei.hibernate.pojos.Order" table="orders">
- <cache usage="read-only"/>
- <id name="id" type="string">
- <column name="id"></column>
- <generator class="uuid"></generator>
- </id>
- <property name="orderNumber" column="orderNumber" type="string"></property>
- <property name="cost" column="cost" type="integer"></property>
- <many-to-one name="customer" class="com.suxiaolei.hibernate.pojos.Customer"
- column="customer_id" cascade="save-update">
- </many-to-one>
- </class>
- </hibernate-mapping>
若存在一對多的關系,想要在在獲取一方的時候將關聯的多方緩存起來,需要再集合屬性下添加<cache>子標簽,這里需要將關聯的對象的hbm文件中必須在存在<class>標簽下也添加<cache>標簽,不然Hibernate只會緩存OID。
- <hibernate-mapping>
- <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">
- <!-- 主鍵設置 -->
- <id name="id" type="string">
- <column name="id"></column>
- <generator class="uuid"></generator>
- </id>
- <!-- 屬性設置 -->
- <property name="username" column="username" type="string"></property>
- <property name="balance" column="balance" type="integer"></property>
- <set name="orders" inverse="true" cascade="all" lazy="false" fetch="join">
- <cache usage="read-only"/>
- <key column="customer_id" ></key>
- <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>
- </set>
- </class>
- </hibernate-mapping>
原文鏈接:http://www.cnblogs.com/otomedaybreak/archive/2012/01/20/2328317.html
【編輯推薦】