一、簡介
- 二級緩存也稱進(jìn)程級的緩存或SessionFactory級的緩存,二級緩存可以被所有的session共享。二級緩存的生命周期和SessionFactory的一致,SessionFactory可以管理二級緩存。
- 本身hibernate也提供二級緩存的工具,但是一般我們在實(shí)際開發(fā)中不使用,而是使用一些外部的二級緩存工具,這里介紹EhCache。
二、配置和使用
- 1.首先當(dāng)然是加入相關(guān)的jar包,同時將配置文件
ehcache.xml加入到src下。 - 2.開啟二級緩存,修改
hibernate.cfg.xml文件 - 3.指定緩存產(chǎn)品提供商
- 4.指定哪些實(shí)體使用二級緩存
hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 下面這些字符串在.property配置文件中都能查到 -->
<property name="hibernate.connection.url">
<![CDATA[jdbc:mysql://localhost:3305/hibernate_cache_level_2?useUnicode=true&characterEncoding=utf8]]>
</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">walp1314</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<!-- 開啟二級緩存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 指定緩存產(chǎn)品提供商 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<mapping resource="cn/itcast/hibernate/Student.hbm.xml"/>
<mapping resource="cn/itcast/hibernate/Classes.hbm.xml"/>
<!-- 指定哪些實(shí)體使用二級緩存 -->
<class-cache class="cn.itcast.hibernate.Student" usage="read-only"/>
</session-factory>
</hibernate-configuration>
說明:
這里我們在指定哪些實(shí)體類使用二級緩存的時候可以在上面的文件中進(jìn)行配置,這是推薦的方式,同時我們也可以在
Student.hbm.xml中的class標(biāo)簽內(nèi)部使用<cache usage="read-only"/>進(jìn)行配置(不推薦)。因?yàn)橐话銓⒆兓淮蟮臄?shù)據(jù)放在二級緩存中,這樣才能發(fā)揮出緩存的功能,所以一般二級緩存的策略使用
read-only。注意:
read-only這種策略表示只讀,但是這樣就不能更改緩存,但是有時候我們修改了數(shù)據(jù)庫數(shù)據(jù)而緩存不能更改確實(shí)會造成錯誤,這里我們可以在ehcache.xml中對緩存的生命周期進(jìn)行配置,一定時間后讓緩存更新。注意:二級緩存也是緩存實(shí)體對象,對普通屬性不進(jìn)行緩存。
相關(guān)實(shí)體:
Student.java
private int id;
private String name;
private Classes classes;
Classes.java
private int id;
private String name;
private Set students;
配置:
Classes.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.hibernate">
<class name="Classes" table="_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" inverse="true" cascade="all">
<key column="classesid"/>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.hibernate.Student" table="_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="classes" column="classesid"/>
</class>
</hibernate-mapping>
ehcache.xml
<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>
測試:
CacheLevel2Test.java
package cn.itcast.hibernate;
import org.hibernate.CacheMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import junit.framework.TestCase;
public class CacheLevel2Test extends TestCase {
/**
* 開啟兩個session,分別調(diào)用load
*/
public void testCache1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不會發(fā)出sql,因?yàn)殚_啟了二級緩存,session是共享二級緩存的
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 開啟兩個session,分別調(diào)用get
*/
public void testCache2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不會發(fā)出sql,因?yàn)殚_啟了二級緩存,session是共享二級緩存的
Student student = (Student)session.get(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
/**
* 開啟兩個session,分別調(diào)用load,在使用SessionFactory清除二級緩存
*/
public void testCache3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
//管理二級緩存
SessionFactory factory = HibernateUtils.getSessionFactory();
//factory.evict(Student.class);
factory.evict(Student.class, 1);
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//會發(fā)出查詢sql,因?yàn)槎壘彺嬷械臄?shù)據(jù)被清除了
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
說明:從上面三個測試中可以發(fā)現(xiàn)load、get方法都是共享二級緩存的。注意第三個測試?yán)又械氖褂肧essionFactory清理緩存,可以指定只清除某一個對象。
一級緩存和二級緩存的交互:
- 若將二級緩存的模式設(shè)置為GET方式則表示load等會去查詢緩存,但是不會向緩存中存數(shù)據(jù);
- 若將二級緩存的模式設(shè)置為PUT方式則表示會向緩存中存數(shù)據(jù),但是不會到緩存中查數(shù)據(jù)。
/**
* 一級緩存和二級緩存的交互
*/
public void testCache4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//僅從二級緩存中讀數(shù)據(jù),而不向二級緩存中寫數(shù)據(jù)
session.setCacheMode(CacheMode.GET);
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//發(fā)出sql語句,因?yàn)閟ession設(shè)置了CacheMode為GET,所以二級緩存中沒有數(shù)據(jù)
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//只向二級緩存寫數(shù)據(jù),而不從二級緩存讀數(shù)據(jù)
session.setCacheMode(CacheMode.PUT);
//會發(fā)出查詢sql,因?yàn)閟ession將CacheMode設(shè)置成了PUT
Student student = (Student)session.load(Student.class, 1);
System.out.println("student.name=" + student.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}