上篇博客中了解一级缓存即session级别的缓存,它的生命周期和session是相同的,并且不同的session之间的缓存是不能共享的。那么二级缓存这个sessionFactory级别的缓存和一级缓存相比又有什么特点呢?
首先需要说明hibernate中二级缓存一般是使用第三方的缓存产品实现的,hibernate中默认集成了EHCache,在进行实例测试前需要先将环境配置起来。
一、将相关的ehcache的jar包引入
二、将ehcache.xml配置文件拷贝到classpath能读取到的路径下
三. 在hibernate.cfg.xml文件中加入ehcache的配置
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
四、在hibernate.cfg.xml中启用二级缓存
<propertyname="hibernate.cache.use_second_level_cache">true</property>
五、指定使用二级缓存的实体类(可以在映射文件中采用<cache>标签指定或在hibernate.cfg.xml文件中统一指定),如下为在hibernate.cfg.xml中进行统一指定:
<class-cacheclass="com.bjpowernode.hibernate.Student"usage="read-only"/>
以上代码指定student实体类可以有二级缓存。其中usage表示的是缓存的策略,read-only表示只读,缓存不更新,适用于不发生改变的数据,效率最高,事务隔离级别最低。还有read-write缓存在数据变化时触发更新,适用于变化的数据。其他的大家可以自行到网上查询了解。
以上5步完成了二级缓存的配置,下面通过一些实例测试了解一下二级缓存的特性。我们知道session是由sessionFactory创建的,那么一级缓存和二级缓存是否存在什么关联呢?
在一级缓存中我们的测试都是在一个session中进行的因为二级缓存是进程级别的缓存,所以二级缓存我们将在不同的session中进行测试,看在同一个进程中不同的session是否共享缓存。
和一级缓存的实例一样,我们先从get和load方法查询一个实体开始。
/**
* 在两个session中发load查询
*/
public void testLoad() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Studentstudent = (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();
Studentstudent = (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);
}
}
执行完毕发现只发出一条sql语句,因为配置了二级缓存,二级缓存是进程级别的缓存,所以不同的session可以共享二级缓存中的数据。使用get也会出现同样的结果。
使用list或iterate查询实体集
/**
* 在两个session中发出两次iterate查询,查询实体对象
*/
public void testIterate() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Iteratoriter = session.createQuery("from Student swhere s.id<5").iterate();
while (iter.hasNext()) {
Studentstudent = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
System.out.println("------------------------------");
try {
session= HibernateUtils.getSession();
session.beginTransaction();
Iteratoriter = session.createQuery("from Student swhere s.id<5").iterate();
while (iter.hasNext()) {
Studentstudent = (Student)iter.next();
System.out.println(student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
发现执行结果和在一级缓存中测试是相同的,第一次查询时iterate会发出1条查询所有id的语句,然后在打印student的name时发出n条根据id查询详细信息的语句一共N+1条。而第二次在另外一个session中查询时只发出一条查询id的语句,其他的数据是从二级缓存中获取的。
使用list查询时同样会发出两条查询所有满足条件的记录。当先使用list查出然后使用另外一个session进行iterate查询时iterate也只发出一条查询id的语句,其他的数据从sessionFactory的缓存中进程获取。
以上的测试实例证明二级缓存和一级缓存在最终的效果上的一样的,如果不进行人为的缓存清除,二级缓存的使用范围更广是在整个进程中的。说到人为清除缓存可以调用sessionFactory的evict()方法。
说明了一级缓存和二级缓存的异同,再通过下面的实例看一下一级缓存和二级缓存有什么联系。Session基本的缓存即一级缓存总是开启的,当我们通过配置开启了二级缓存后一级缓存在关闭时会将数据保存到二级缓存中,所以不同的session之间在二级缓存中可以共享数据,当然我们也可以认为的禁止这一过程,代码如下:
/**
* 开启二级缓存
*
* 禁止一级缓存和二级缓存的交互
*/
public void testCache4() {
Sessionsession = null;
try {
session= HibernateUtils.getSession();
session.beginTransaction();
//禁止将一级缓存中的数据放到二级缓存中
session.setCacheMode(CacheMode.IGNORE);
Studentstudent = (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();
Studentstudent = (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.setCacheMode(CacheMode.IGNORE);禁止了将一级缓存中的数据放入到二级缓存中。
小结
基于二级缓存的特点,适合使用二级缓存的条件大致如下:
1)很少被修改的数据
2)不是很重要的数据,允许出现偶尔并发的数据
3)不会被并发访问的数据
一级和二级缓存在总计的最开始我们就说道他们只对查询实体或实体集进行缓存,那么如果查询普通的属性如何进行缓存呢?有会有什么特点,下篇博客学习一下查询缓存。