一、简介
- 二级缓存也称进程级的缓存或SessionFactory级的缓存,二级缓存可以被所有的session共享。二级缓存的生命周期和SessionFactory的一致,SessionFactory可以管理二级缓存。
- 本身hibernate也提供二级缓存的工具,但是一般我们在实际开发中不使用,而是使用一些外部的二级缓存工具,这里介绍EhCache。
二、配置和使用
- 1.首先当然是加入相关的jar包,同时将配置文件
ehcache.xml
加入到src下。 - 2.开启二级缓存,修改
hibernate.cfg.xml
文件 - 3.指定缓存产品提供商
- 4.指定哪些实体使用二级缓存
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>
<!-- 指定缓存产品提供商 -->
<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"/>
<!-- 指定哪些实体使用二级缓存 -->
<class-cache class="cn.itcast.hibernate.Student" usage="read-only"/>
</session-factory>
</hibernate-configuration>
说明:
这里我们在指定哪些实体类使用二级缓存的时候可以在上面的文件中进行配置,这是推荐的方式,同时我们也可以在
Student.hbm.xml
中的class标签内部使用<cache usage="read-only"/>
进行配置(不推荐)。因为一般将变化不大的数据放在二级缓存中,这样才能发挥出缓存的功能,所以一般二级缓存的策略使用
read-only
。注意:
read-only
这种策略表示只读,但是这样就不能更改缓存,但是有时候我们修改了数据库数据而缓存不能更改确实会造成错误,这里我们可以在ehcache.xml
中对缓存的生命周期进行配置,一定时间后让缓存更新。注意:二级缓存也是缓存实体对象,对普通属性不进行缓存。
相关实体:
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,分别调用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();
//不会发出sql,因为开启了二级缓存,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,分别调用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();
//不会发出sql,因为开启了二级缓存,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,分别调用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();
//会发出查询sql,因为二级缓存中的数据被清除了
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);
}
}
}
说明:从上面三个测试中可以发现load、get方法都是共享二级缓存的。注意第三个测试例子中的使用SessionFactory清理缓存,可以指定只清除某一个对象。
一级缓存和二级缓存的交互:
- 若将二级缓存的模式设置为GET方式则表示load等会去查询缓存,但是不会向缓存中存数据;
- 若将二级缓存的模式设置为PUT方式则表示会向缓存中存数据,但是不会到缓存中查数据。
/**
* 一级缓存和二级缓存的交互
*/
public void testCache4() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//仅从二级缓存中读数据,而不向二级缓存中写数据
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();
//发出sql语句,因为session设置了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();
//只向二级缓存写数据,而不从二级缓存读数据
session.setCacheMode(CacheMode.PUT);
//会发出查询sql,因为session将CacheMode设置成了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);
}
}