1. 什麼是緩存? 資料庫的緩存指的是應用程式和物理數據源之間的數據。即把物理數據源的數據複製到緩存。有了緩存,可以降低應用程式對物理數據源的訪問頻率,從而提高效率。緩存的介質一般是記憶體,也可以是硬碟。 Hibernate的緩存有三種類型:一級緩存、二級緩存和查詢緩存。 2. 一級緩存 一級緩存即S ...
1. 什麼是緩存?
資料庫的緩存指的是應用程式和物理數據源之間的數據。即把物理數據源的數據複製到緩存。有了緩存,可以降低應用程式對物理數據源的訪問頻率,從而提高效率。緩存的介質一般是記憶體,也可以是硬碟。
Hibernate的緩存有三種類型:一級緩存、二級緩存和查詢緩存。
2. 一級緩存
一級緩存即Session緩存,由Session自動進行管理,不需要程式進行干預。一級緩存根據對象的ID進行載入和緩存。如下麵的代碼:
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Course c = (Course) session.get(Course.class, 1); System.out.println("Name:" + c.getName()); c = (Course) session.get(Course.class, 1); System.out.println("Name:" + c.getName()); tx.commit(); session.close(); }
運行結果:
Hibernate:
select
course0_.ID as ID0_0_,
course0_.NAME as NAME0_0_,
course0_.COMMENT as COMMENT0_0_
from
clas course0_
where
course0_.ID=?
Name:電腦原理
Name:電腦原理
第1次查詢時生成了SQL語句,並將查詢出來的對象放在一級緩存裡面,第2次查詢時,在一級緩存裡面直接找到了這個對象,就不需要再次生成SQL語句了。
再看一個例子:
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Course c = (Course) session.get(Course.class, 1); System.out.println("Name:" + c.getName()); tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); c = (Course) session.get(Course.class, 1); System.out.println("Name:" + c.getName()); tx.commit(); session.close(); }
由於一級緩存是Session級別的緩存,所以Session關閉以後,一級緩存也就不存在了,第2次查詢也要生成SQL語句:
Hibernate:
select
course0_.ID as ID0_0_,
course0_.NAME as NAME0_0_,
course0_.COMMENT as COMMENT0_0_
from
clas course0_
where
course0_.ID=?
Name:電腦原理
Hibernate:
select
course0_.ID as ID0_0_,
course0_.NAME as NAME0_0_,
course0_.COMMENT as COMMENT0_0_
from
clas course0_
where
course0_.ID=?
Name:電腦原理
3. 二級緩存
二級緩存即SessionFactory緩存,和一級緩存類似,也是根據對象的ID進行載入和緩存,區別就在於一級緩存只在Session內有效,而二級緩存在SessionFactory內有效。在訪問某個ID的對象時,先到一級緩存裡面去找,如果沒有找到就到二級緩存裡面去找。二級緩存包括EHCache,OSCache,SwarmCache和JBossCache等。這裡以EHCache作為例子。
二級緩存需要程式進行管理。首先,配置Maven下載相關的Jar,在pom文件裡面添加:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>4.1.0.Final</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.8.3</version> </dependency>
創建EHCache配置文件ehcache.xml:
<ehcache> <diskStore path="E:\Eclipse\MyWorkspace\Cache"/> <defaultCache maxElementsInMemory="10000" eternal="true" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <cache name="com.hzhi.course.entity.Course" maxElementsInMemory="10000" eternal="true" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true" /> </ehcache>
defaultCache是預設的設置,下麵一個cache指明瞭對哪一個類進行二級緩存。裡面設置了最大緩存的對象數量,是否永久有效、最大空閑秒數、最大生存秒數、記憶體滿時是否寫到硬碟,寫到硬碟的路徑等等。
修改需要緩存的類的hbm文件:
<class name="com.hzhi.course.entity.Course" table="clas"> <cache usage="read-only"/> ...... </class>
usage設置了併發訪問策略,一般設置成read-only。
修改applicationContext.xml中的SessionFactory的配置,增加二級緩存的一些屬性:
<!-- SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" > <ref local="dataSource"/> </property> <!-- 配置Hibernate的屬性 --> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.connection.isolation">8</prop> <!-- 二級緩存 --> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop> <prop key="hibernate.cache.provider_configuration_file_resource_path">WEB-INF/ehcache.xml</prop> </props> </property>
......
</bean>
運行下麵的例子:
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Course c = (Course) session.get(Course.class, 1); System.out.println("Name:" + c.getName()); tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); c = (Course) session.get(Course.class, 1); System.out.println("Name:" + c.getName()); tx.commit(); session.close(); }
結果:
Hibernate:
select
course0_.ID as ID0_0_,
course0_.NAME as NAME0_0_,
course0_.COMMENT as COMMENT0_0_
from
clas course0_
where
course0_.ID=?
Name:電腦原理
Name:電腦原理
雖然關閉了Session,但是二級緩存仍然存在,所以只生成了一次SQL語句。
下麵的例子:
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery("from Course"); Iterator iter = query.iterate(); while(iter.hasNext()){ System.out.println(((Course)iter.next()).getName()); } tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); query = session.createQuery("from Course"); iter = query.iterate(); while(iter.hasNext()){ System.out.println(((Course)iter.next()).getName()); } tx.commit(); session.close(); }
結果:
Hibernate: select course0_.ID as col_0_0_ from clas course0_ Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 電腦原理 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 電腦網路 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 資料庫原理 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? C語言 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 大學英語A Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Java Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Linux Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 高等數學 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 語文 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 大學物理 Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 軟體工程 Hibernate: select course0_.ID as col_0_0_ from clas course0_ 電腦原理 電腦網路 資料庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟體工程
當使用Query的list()方法時,只生成一次SQL語句查詢出所有的對象,使用iterate()方法時,會先得到所有對象的ID,然後根據每個ID生成一次SQL語句查詢。第二個Session裡面使用的也是iterate()方法,首先生成一次SQL語句,得到ID,然後根據ID查找對象,由於開啟了二級緩存,在二級緩存裡面找到了對象,所以就直接輸出了,並沒有再根據每個ID生成SQL語句。
不論是一級緩存還是二級緩存,都只能緩存對象,不能緩存屬性的值。下麵的例子:
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery("select c.name from Course c"); List<String> names = query.list(); for(Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println("----------"); query = session.createQuery("select c.name from Course c"); names = query.list(); for(Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println("----------"); tx.commit(); session.close(); }
運行結果:
Hibernate:
select
course0_.NAME as col_0_0_
from
clas course0_
電腦原理
電腦網路
資料庫原理
C語言
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------
Hibernate:
select
course0_.NAME as col_0_0_
from
clas course0_
電腦原理
電腦網路
資料庫原理
C語言
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------
雖然開啟了二級緩存,但是查詢的結果不是對象,是屬性,所以並沒有緩存,第2次查詢仍然生成了查詢語句。要解決這個問題,就需要查詢緩存。
3. 查詢緩存
在配置了二級緩存的基礎上,可以設置查詢緩存,在SessionFactory的設置裡面加上一行:
<prop key="hibernate.cache.use_query_cache">true</prop>
即打開了查詢緩存。查詢緩存也是SessionFactory級別的緩存,在整個SessionFactory裡面都是有效的。
關閉二級緩存,運行下麵的例子,在Query後面添加setCacheable(true)打開查詢緩存:
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery("select c.name from Course c"); query.setCacheable(true); List<String> names = query.list(); for(Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println("----------"); query = session.createQuery("select c.name from Course c"); query.setCacheable(true); names = query.list(); for(Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println("----------"); tx.commit(); session.close(); }
結果:
Hibernate:
select
course0_.NAME as col_0_0_
from
clas course0_
電腦原理
電腦網路
資料庫原理
C語言
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------
電腦原理
電腦網路
資料庫原理
C語言
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------
由於兩次查詢的HQL語句是一致的,所以只生成一次SQL語句。但是如果把第二次查詢改一下:
System.out.println("----------"); query = session.createQuery("select c.name from Course c where c.id > 5"); query.setCacheable(true); names = query.list(); for(Iterator iter = names.iterator(); iter.hasNext();){ String name = (String) iter.next(); System.out.println(name); } System.out.println("----------");
結果:
Hibernate:
select
course0_.NAME as col_0_0_
from
clas course0_
電腦原理
電腦網路
資料庫原理
C語言
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------
Hibernate:
select
course0_.NAME as col_0_0_
from
clas course0_
where
course0_.ID>5
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------
由於HQL語句變了,所以第二次也生成了SQL語句。
查詢緩存可以緩存屬性,也可以緩存對象,但是當緩存對象時,只緩存對象的ID,不會緩存整個對象。下麵的例子:
@Override public void testCache() { // TODO Auto-generated method stub Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Query query = session.createQuery("from Course"); query.setCacheable(true); List<Course> list = query.list(); for (int i=0; i<list.size(); i++){ System.out.println(list.get(i).getName()); } System.out.println("----------"); tx.commit(); session.close(); session = sessionFactory.openSession(); tx = session.beginTransaction(); query = session.createQuery("from Course"); query.setCacheable(true); list = query.list(); for (int i=0; i<list.size(); i++){ System.out.println(list.get(i).getName()); } System.out.println("----------"); tx.commit(); session.close(); }
結果:
Hibernate: select course0_.ID as ID0_, course0_.NAME as NAME0_, course0_.COMMENT as COMMENT0_ from clas course0_ 電腦原理 電腦網路 資料庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟體工程 ---------- Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? Hibernate: select course0_.ID as ID0_0_, course0_.NAME as NAME0_0_, course0_.COMMENT as COMMENT0_0_ from clas course0_ where course0_.ID=? 電腦原理 電腦網路 資料庫原理 C語言 大學英語A Java Linux 高等數學 語文 大學物理 軟體工程 ----------
由於開了查詢緩存,沒有開二級緩存,雖然使用的是list()方法一次查詢出了所有的對象,但是查詢緩存只緩存了對象ID,沒有緩存整個對象。所以在第2個Session裡面"from Course"這個HQL由於和前面的相同,並沒有生成SQL語句,但是由於沒有開二級緩存,沒有緩存整個對象,只能根據每個ID去生成一次SQL語句。雖然兩次用的都是list()方法,但是第一次是生成SQL語句去一次查詢出所有的對象,而第二次是根據查詢緩存裡面的ID一個一個的生成SQL語句。
如果同時打開查詢緩存和二級緩存,第2個Session裡面就不用再根據ID去生成SQL語句了:
Hibernate:
select
course0_.ID as ID0_,
course0_.NAME as NAME0_,
course0_.COMMENT as COMMENT0_
from
clas course0_
電腦原理
電腦網路
資料庫原理
C語言
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------
電腦原理
電腦網路
資料庫原理
C語言
大學英語A
Java
Linux
高等數學
語文
大學物理
軟體工程
----------