1、why(爲何要用Hibernate緩存?) java
Hibernate是一個持久層框架,常常訪問物理數據庫。爲了下降應用程序對物理數據源訪問的頻次,從而提升應用程序的運行性能。
2、what(Hibernate緩存原理是怎樣的?)Hibernate緩存包括兩大類:Hibernate一級緩存和Hibernate二級緩存。 sql
1.Hibernate一級緩存又稱爲「Session的緩存」。
Session內置不能被卸載,Session的緩存是事務範圍的緩存(Session對象的生命週期一般對應一個數據庫事務或者一個應用事務)。一級緩存中,持久化類的每一個實例都具備惟一的OID。
2.Hibernate二級緩存又稱爲「SessionFactory的緩存」。
因爲SessionFactory對象的生命週期和應用程序的整個過程對應,所以Hibernate二級緩存是進程範圍或者集羣範圍的緩存,有可能出現併發問題,所以須要採用適當的併發訪問策略,該策略爲被緩存的數據提供了事務隔離級別。
第二級緩存是可選的,是一個可配置的插件,默認下SessionFactory不會啓用這個插件。
Hibernate提供了org.hibernate.cache.CacheProvider接口,它充當緩存插件與Hibernate之間的適配器。
適合存放到第二級緩存中的數據?
(1) 不多被修改的數據
(2) 不是很重要的數據,容許出現偶爾併發的數據
(3) 不會被併發訪問的數據
(4) 常量數據
不適合存放到第二級緩存的數據?
(1) 常常被修改的數據
(2) 絕對不容許出現併發訪問的數據,如財務數據,絕對不容許出現併發
(3) 與其餘應用共享的數據 數據庫
Hibernate session就是java.sql.Connection的一層高級封裝,一個session對應了一個Connection。
http請求結束後正確的關閉session(過濾器實現了session的正常關閉);延遲加載必須保證是同一個session(session綁定在ThreadLocal)。 緩存
4.Hibernate查找對象如何應用緩存?
當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查; session
查不到,若是配置了二級緩存,那麼從二級緩存中查; 併發
若是都查不到,再查詢數據庫,把結果按照ID放入到緩存刪除、更新、增長數據的時候,同時更新緩存。 app
3、how(Hibernate的緩存機制如何應用?) 框架
1. 一級緩存的管理:2. 一級緩存應用: ide
save(): 當session對象調用save()方法保存一個對象後,該對象會被放入到session的緩存中。
get()和load(): 當session對象調用get()或load()方法從數據庫取出一個對象後,該對象也會被放入到session的緩存中。 使用HQL和QBC等從數據庫中查詢數據。 性能
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先檢索緩存中是否有該查找對象,發現正好有該查找對象,就從緩存中取出來,再也不去數據庫 中檢索。 3. 二級緩存的管理:
4. 二級緩存的配置
經常使用的二級緩存插件
EHCache org.hibernate.cache.EhCacheProvider
OSCache org.hibernate.cache.OSCacheProvider
SwarmCahe org.hibernate.cache.SwarmCacheProvider
JBossCache org.hibernate.cache.TreeCacheProvider
<!-- EHCache的配置,hibernate.cfg.xml --> <hibernate-configuration> <session-factory> <!-- 設置二級緩存插件EHCache的Provider類--> <property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property> <!-- 啓動"查詢緩存" --> <property name="hibernate.cache.use_query_cache"> true </property> </session-factory> </hibernate-configuration>
<!-- 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.xml --> <?xml version="1.0" encoding='UTF-8'?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> <class> <!-- 設置該持久化類的二級緩存併發訪問策略 read-only read-write nonstrict-read-write transactional--> <cache usage="read-write"/> </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>