Hibernate中提供了兩級緩存,一級緩存是Session級別的緩存,它屬於事務範圍的緩存,該級緩存由hibernate管理,應用程序無需干預;二級緩存是SessionFactory級別的緩存,該級緩存可以進行配置和更改,並且可以動態加載和卸載,hibernate還爲查詢結果提供了一個查詢緩存,它依賴於二級緩存;
一,緩存的概念
緩存是位於應用程序和永久性數據存儲源之間用於臨時存放複製數據的內存區域,緩存可以降低應用程序之間讀寫永久性數據存儲源的次數,從而提高應用程序的運行性能;
hibernate在查詢數據時,首先會到緩存中查找,如果找到就直接使用,找不到時才從永久性數據存儲源中檢索,因此,把頻繁使用的數據加載到緩存中,可以減少應用程序對永久性數據存儲源的訪問,使應用程序的運行性能得以提升;
二,緩存的範圍
緩存範圍決定了緩存的生命週期,緩存範圍分爲3類:
1>事務範圍
緩存只能被當前事務訪問,緩存的生命週期依賴於事務的生命週期,事務結束時,緩存的生命週期也結束了;
2>進程範圍
緩存被進程內的所有事務共享,這些事務會併發訪問緩存,需要對緩存採用必要的事務隔離機制,緩存的生命週期取決與進程的生命週期,進程結束,緩存的生命週期也結束了;
3>集羣範圍
緩存被一個或多個計算機的進程共享,緩存中的數據被複制到集羣中的每個進行節點,進程間通過遠程通信來保證緩存中數據的一致性;
在查詢時,如果在事務範圍內的緩存中沒有找到,可以到進程範圍或集羣範圍的緩存中查找,如果還沒找到,則到數據庫中查詢;
三,Hibernate中的第一級緩存
Hibernate的一級緩存由Session提供,只存在於Session的生命週期中,當應用程序調用Session接口的save(),update(),saveOrupDate(),get(),load()或者Query和Criteria實例的list(),iterate()等方法時,如果Session緩存中沒有相應的對象,hibernate就會把對象加入到一級緩存中,當session關閉時,該Session所管理的一級緩存也會立即被清除;
1,get查詢測試:
1>在同一個session中發出兩次get查詢
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess = HibernateSessionFactory.getSession();
- Transaction tx = sess.beginTransaction();
- Student s1 = (Student)sess.get(Student.class, 2);
- System.out.println(s1.getName());
- Student s2 = (Student)sess.get(Student.class, 2);
- System.out.println(s2.getName());
- tx.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
2>開啓兩個session中發出兩次get查詢
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess1 = HibernateSessionFactory.getSession();
- Transaction tx1 = sess1.beginTransaction();
- Student s1 = (Student)sess1.get(Student.class, 2);
- System.out.println(s1.getName());
- tx1.commit();
- HibernateSessionFactory.closeSession();
- Session sess2 = HibernateSessionFactory.getSession();
- Transaction tx2 = sess2.beginTransaction();
- Student s2 = (Student)sess2.get(Student.class, 2);
- System.out.println(s2.getName());
- tx2.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
2,iterate查詢測試
插入一個iteritor查詢方式:
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess = HibernateSessionFactory.getSession();
- Transaction tx = sess.beginTransaction();
- Query query = sess.createQuery("from Student");
- Iterator iter = query.iterate();
- while(iter.hasNext()){
- System.out.println(((Student)iter.next()).getName());
- }
- tx.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess = HibernateSessionFactory.getSession();
- Transaction tx = sess.beginTransaction();
- Student s1 = (Student)sess.createQuery("from Student s where s.id = 2").iterate().next();
- System.out.println(s1.getName());
- Student s2 = (Student)sess.createQuery("from Student s where s.id = 2").iterate().next();
- System.out.println(s2.getName());
- tx.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
3,iterate查詢屬性測試:
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess = HibernateSessionFactory.getSession();
- Transaction tx = sess.beginTransaction();
- String name1 = (String)sess.createQuery("select s.name from Student s where s.id = 2").iterate().next();
- System.out.println(name1);
- String name2 = (String)sess.createQuery("select s.name from Student s where s.id = 2").iterate().next();
- System.out.println(name2);
- tx.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
4,在一個session中先save,再執行load查詢
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess = HibernateSessionFactory.getSession();
- Transaction tx = sess.beginTransaction();
- Student s = new Student(8, "newAcc", 8, 8);
- Serializable id = sess.save(s);
- tx.commit();
- Student s1 = (Student)sess.load(Student.class, 8);
- System.out.println(s1.getName());
- HibernateSessionFactory.closeSession();
- }</strong></span>
--------------------
Session接口爲應用程序提供了兩個管理緩存的方法:
1>evict()方法:用於將某個對象從Session的一級緩存中清除;
2>clear()方法:用於將一級緩存中的所有對象全部清楚;
測試:
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess = HibernateSessionFactory.getSession();
- Transaction tx = sess.beginTransaction();
- Student s = (Student)sess.load(Student.class, 1);
- System.out.println(s.getName());
- sess.clear();//清除一級緩存中的所有對象
- Student s1 = (Student)sess.load(Student.class, 1);
- System.out.println(s1.getName());
- tx.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
四,Hibernate中的第二級緩存
二級緩存是一個可插拔的緩存插件,它是由SessionFactory負責管理的;
由於SessionFactory對象的生命週期與應用程序的整個過程對應,通常一個應用程序對應一個SessionFactory,因此,二級緩存是進程範圍或者集羣範圍的緩存;
與一級緩存一樣,二級緩存也是根據對象的id來加載與緩存,當執行某個查詢獲得結果集爲實體對象集時,hibernate就會把它們按照對象id加載到二級緩存中,在訪問指定的id的對象時,首先從一級緩存中查找,找到就直接使用,找不到則轉到二級緩存中查找(必須配置且啓用二級緩存),如果二級緩存中找到,則直接使用,否則會查詢數據庫,並將查詢結果根據對象的id放到緩存中;
1,常用的二級緩存插件
Hibernate的二級緩存功能是通過配置二級緩存插件來實現的,常用的二級緩存插件包括EHCache,OSCache,SwarmCache和JBossCache。其中EHCache緩存插件是理想的進程範圍的緩存實現,此處以使用EHCache緩存插件爲例,來介紹如何使用hibernate的二級緩存;
2,Hibernate中使用EHCache的配置
1>引入EHCache相關的jar包;
lib\optional\ehcache下的三個jar包;
2>創建EHCache的配置文件ehcache.xml
- <span style="font-size:18px;"><strong><ehcache>
- <diskStore path="java.io.tmpdir"/>
- <defaultCache
- maxElementsInMemory="10000"
- eternal="false"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- overflowToDisk="true"
- />
- <cache name="sampleCache1"
- maxElementsInMemory="10000"
- eternal="false"
- timeToIdleSeconds="300"
- timeToLiveSeconds="600"
- overflowToDisk="true"
- />
- <cache name="sampleCache2"
- maxElementsInMemory="1000"
- eternal="true"
- timeToIdleSeconds="0"
- timeToLiveSeconds="0"
- overflowToDisk="false"
- />
- </ehcache></strong></span>
在defaultCache元素中,maxElementsInMemory屬性設置緩存對象的最大數目;eternal屬性指定是否永不過期,true爲不過期,false爲過期;timeToldleSeconds屬性設置對象處於空閒狀態的最大秒數;timeToLiveSeconds屬性設置對象處於緩存狀態的最大秒數;overflowToDisk屬性設置內存溢出時是否將溢出對象寫入硬盤;
3>在Hibernate配置文件裏面啓用EHCache
在hibernate.cfg.xml配置文件中,啓用EHCache的配置如下:
- <span style="font-size:18px;"><strong> <!-- 啓用二級緩存 -->
- <property name="hibernate.cache.use_second_level_cache">true</property>
- <!-- 設置二級緩存插件EHCache的Provider類 -->
- <property name="hibernate.cache.region.factory_class">
- org.hibernate.cache.ehcache.EhCacheRegionFactory</property></strong></span>
1>>在實體類的映射文件裏面配置
在需要進行緩存的持久化對象的映射文件中配置相應的二級緩存策略,如User,hbm.xml:
- <span style="font-size:18px;"><strong><?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 name="com.hibtest1.entity.User" table="user" catalog="bookshop">
- <cache usage="read-write"/>
- <id name="id" type="java.lang.Integer">
- <column name="Id" />
- <generator class="native" />
- </id>
- <property name="loginName" type="java.lang.String">
- <column name="LoginName" length="50" />
- </property>
- </class>
- </hibernate-mapping></strong></span>
注意:<cache>元素只能放在<class>元素的內部,而且必須處在<id>元素的前面,<cache>元素放在哪些<class>元素下面,就說明會對哪些類進行緩存;
2>>在hibernate配置文件中統一配置,強烈推薦使用這種方式:
在hibernate.cfg.xml文件中使用<class-cache>元素來配置哪些實體類的對象需要二級緩存:
- <span style="font-size:18px;"><strong><span style="font-size:18px;"><strong><span style="font-size:18px;"><strong><class-cache usage="read-only" class="com.anlw.entity.Student"/></strong></span></strong></span></strong></span>
3,Hibernate中使用EHCache的測試:
- <span style="font-size:18px;"><strong> public void Query(){
- Session sess1 = HibernateSessionFactory.getSession();
- Student s1 = (Student)sess1.get(Student.class, 1);
- System.out.println(s1.getName());
- HibernateSessionFactory.closeSession();
- Session sess2 = HibernateSessionFactory.getSession();
- Student s2 = (Student)sess2.get(Student.class, 1);
- System.out.println(s2.getName());
- HibernateSessionFactory.closeSession();
- }</strong></span>
注意:在hibernate配置二級緩存時屬性的順序如下,順序錯了會空指針異常:
- <span style="font-size:18px;"><strong> <!-- 啓用二級緩存 -->
- <property name="hibernate.cache.use_second_level_cache">true</property>
- <!-- 設置二級緩存插件EHCache的Provider類 -->
- <property name="hibernate.cache.region.factory_class">
- org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
- <mapping class="com.anlw.entity.Student"/>
- <class-cache usage="read-only" class="com.anlw.entity.Student"/></strong></span>
五,Hibernate中的查詢緩存
對於經常使用的查詢語句,如果啓用了查詢緩存 ,當第一次執行查詢語句時,hibernate會將查詢結果存儲在二級緩存中,以後再次執行該查詢語句時,從緩存中獲取查詢結果,從而提高查詢性能;
hibernate的查詢緩存主要是針對普通屬性結果集的緩存,而對於實體對象的結果集只緩存id;
查詢緩存的生命週期,若當前關聯的表發生修改,那麼查詢緩存的生命週期結束;
1,查詢緩存的配置
查詢緩存基於二級緩存,使用查詢緩存前,必須首先配置好二級緩存;
在配置了二級緩存的基礎上,在hibernate的配置文件hibernate.cfg.xml中添加如下配置,可以啓用查詢緩存:
<property name="hibernate.cache.use_query_cache">false</property>
此外在程序中還必須手動啓用查詢緩存:
query.setCacheable(true);
2,測試查詢緩存:
1>開啓查詢緩存,關閉二級緩存,開啓一個session,分別調用query.list查詢屬性,測試前,先在先在hibernate.cfg.xml文件中開啓查詢緩存,關閉二級緩存,如下所示:
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
- <span style="font-size:18px;"><strong> public static void query(){
- Session sess = HibernateSessionFactory.getSession();
- Transaction tx = sess.beginTransaction();
- Query query = sess.createQuery("select s.name from Student s");
- query.setCacheable(true);
- List names = query.list();
- for(Iterator iter = names.iterator();iter.hasNext();){
- String name = (String)iter.next();
- System.out.println(name);
- }
- System.out.println("----------");
- query = sess.createQuery("select s.name from Student s");
- query.setCacheable(true);
- names = query.list();
- for(Iterator iter = names.iterator();iter.hasNext();){
- String name = (String)iter.next();
- System.out.println(name);
- }
- tx.commit();
- HibernateSessionFactory.closeSession();
- }
- </strong></span>
第二次沒有去查數據庫,因爲啓用了查詢緩存;
2>開啓查詢緩存,關閉二級緩存,開啓兩個session,分別調用query.list查詢屬性,測試前,先在先在hibernate.cfg.xml文件中開啓查詢緩存,關閉二級緩存,如下所示:
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
- <span style="font-size:18px;"><strong> public static void query(){
- Session sess1 = HibernateSessionFactory.getSession();
- Transaction tx1 = sess1.beginTransaction();
- Query query = sess1.createQuery("select s.name from Student s");
- query.setCacheable(true);
- List names = query.list();
- for(Iterator iter = names.iterator();iter.hasNext();){
- String name = (String)iter.next();
- System.out.println(name);
- }
- tx1.commit();
- HibernateSessionFactory.closeSession();
- System.out.println("----------");
- Session sess2 = HibernateSessionFactory.getSession();
- Transaction tx2 = sess2.beginTransaction();
- query = sess2.createQuery("select s.name from Student s");
- query.setCacheable(true);
- names = query.list();
- for(Iterator iter = names.iterator();iter.hasNext();){
- String name = (String)iter.next();
- System.out.println(name);
- }
- tx2.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
3>開啓查詢緩存,關閉二級緩存,開啓兩個session,分別調用query.list查詢實體對象,測試前,先在先在hibernate.cfg.xml文件中開啓查詢緩存,關閉二級緩存,如下所示:
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
- <span style="font-size:18px;"><strong> public static void query(){
- Session sess1 = HibernateSessionFactory.getSession();
- Transaction tx1 = sess1.beginTransaction();
- Query query = sess1.createQuery("from Student");
- query.setCacheable(true);
- List student = query.list();
- for(Iterator iter = student.iterator();iter.hasNext();){
- Student s = (Student)iter.next();
- System.out.println(s.getName()+"--"+s.getAge());
- }
- tx1.commit();
- HibernateSessionFactory.closeSession();
- System.out.println("----------");
- Session sess2 = HibernateSessionFactory.getSession();
- Transaction tx2 = sess2.beginTransaction();
- query = sess2.createQuery("from Student");
- query.setCacheable(true);
- student = query.list();
- for(Iterator iter = student.iterator();iter.hasNext();){
- Student s = (Student)iter.next();
- System.out.println(s.getName()+"--"+s.getAge());
- }
- tx2.commit();
- HibernateSessionFactory.closeSession();
- }</strong></span>
- <span style="font-size:18px;"><strong>Hibernate:
- select
- student0_.id as id1_0_,
- student0_.age as age2_0_,
- student0_.name as name3_0_
- from
- student student0_
- anliwenaaa--1
- test--2
- ----------
- Hibernate:
- select
- student0_.id as id1_0_0_,
- student0_.age as age2_0_0_,
- student0_.name as name3_0_0_
- from
- student student0_
- where
- student0_.id=?
- Hibernate:
- select
- student0_.id as id1_0_0_,
- student0_.age as age2_0_0_,
- student0_.name as name3_0_0_
- from
- student student0_
- where
- student0_.id=?
- anliwenaaa--1
- test--2</strong></span>
4>開啓查詢緩存,開啓二級緩存,開啓兩個session,分別調用query.list查詢實體對象,測試前,先在先在hibernate.cfg.xml文件中開啓查詢緩存,開啓二級緩存,如下所示:
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.cache.use_second_level_cache">true</property>