在《Hibernate二级缓存的并发访问策略和常用插件》教程介绍的几种常用的二级缓存插件中,EHCache 缓存插件是理想的进程范围的缓存插件。本小节将以 EHCache 缓存插件为例,介绍二级缓存的配置和使用。本节在教程前几节实例的基础上进行整合,具体步骤如下:

1. 引入 EHCache 相关的 JAR 包

读者可以从官方网址 http://www.ehcache.org/downloads/ 中下载 EHCache 的 JAR 包,成功访问后的页面显示如图 1 所示。

27-Hibernate整合EHCache插件实现二级缓存 - 图1
图 1 EHCache的下载

从图 1 中可以看出,目前 EHCache 的最新版本为 ehcache-3.7,本教程使用的是 ehcache-2.10.5,单击图中 Ehcache 2.x 下方的链接,即可下载此版本的 EHCache。下载并解压后,在压缩包文件中找到 ehcache-2.10.5.jar,将其复制到 hibernateDemo05 项目的 lib 目录中,并发布到类路径下即可。

2. 引入 EHCache 的配置文件 ehcache.xml

读者可以直接从 Hibernate 的解压包的 hibernate-distribution-3.6.10.Final\project\etc 目录中找到 ehcache.xml,找到后,将此文件复制到项目的 src 目录下。ehcache.xml 文件中的主要代码如下所示:

  1. <?xml version=”1.0” encoding=”UTF-8”?>
  2. <ehcache xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
  3. xsi:noNamespaceSchemaLocation=”http://ehcache.org/ehcache.xsd
  4. updateCheck=”false”>
  5. <diskStore path=”java.io.tmpdir/Tmp_EhCache” />
  6. <defaultCache
  7. eternal=”false”
  8. maxElementsInMemory=”10000”
  9. overflowToDisk=”false”
  10. diskPersistent=”false”
  11. timeToIdleSeconds=”1800”
  12. timeToLiveSeconds=”259200”
  13. memoryStoreEvictionPolicy=”LRU” />

  14. <cache name=”cloud_user”
  15. eternal=”false”
  16. maxElementsInMemory=”5000”
  17. overflowToDisk=”false”
  18. diskPersistent=”false”
  19. timeToIdleSeconds=”1800”
  20. timeToLiveSeconds=”1800”
  21. memoryStoreEvictionPolicy=”LRU” />

在上述配置代码中,各个元素作用如下表所示:

名称 说明
元素 用于设置缓存数据文件的存储目录
元素 用于设置缓存的默认数据过期策略。
元素 用于设置具体的命名缓存的数据过期策略。
maxElementsInMemory 属性 用于设置缓存对象的最大数目
eternal 属性 用于指定是否永不过期,true 为不过期,false 为过期。
timeToIdleSeconds 属性 用于设置对象处于空闲状态的最大秒数。
timeToLiveSeconds 属性 用于设置对象处于缓存状态的最大秒数。
overflowToDisk 属性 用于设置内存溢出时是否将溢出对象写入硬盘。

3. 启用二级缓存

在 Hibernate 的核心配置文件中启用二级缓存,并指定哪些实体类需要存储到二级缓存中。其配置代码如下所示:

  1. <property name =”hibernate.cache.use_second_level_cache”>true
  2. <property name =”hibernate.cache.provider_class”>
  3. org.hibernate.cache.EhCacheProvider
  4. </property >
  5. <mapping resource =”com/mengma/domain/Person.hbm.xml”/>
  6. <class-cache usage =”read-write” class =”com.mengma.domain.Person”/>

在上述配置代码中,hibernate.cache.use_second_level_cache 用于开启二级缓存,hibernate.cache.provider_class 用于指定二级缓存的供应商。

标签用于指定将哪些数据存储到二级缓存中,其中 usage 属性表示指定缓存策略。需要注意的是, 标签必须放在 标签的后面。

4. 创建测试类

在 com.mengma.test 包下创建一个名为 SecondEHChcheTest 的测试类,并在类中添加一个 testCache() 方法。在 testCache() 方法中,需要开启两个 Session 对象,然后使用 get() 方法查询四次,通过比较查询结果,观察二级缓存的使用情况。其实现代码如下所示。

  1. package com.mengma.test;

  2. import org.hibernate.Session;
  3. import org.hibernate.Transaction;
  4. import org.junit.Test;

  5. import com.mengma.domain.Person;
  6. import com.mengma.utils.HibernateUtils;

  7. public class SecondEHChcheTest {
  8. @Test
  9. public void testCache() {
  10. Session session1 = HibernateUtils.getSession(); // 开启第一个Session对象
  11. Transaction tx1 = session1.beginTransaction(); // 开启第一个事务
  12. Person p1 = (Person) session1.get(Person.class, 1); // 获取p1对象
  13. Person p2 = (Person) session1.get(Person.class, 1); // 获取p2对象
  14. System.out.println(p1 == p2); // 第一次比较
  15. tx1.commit(); // 提交事务
  16. session1.close(); // sesison1对象关闭,一级缓存被清理
  17. Session session2 = HibernateUtils.getSession(); // 开启第二个Session对象
  18. Transaction tx2 = session2.beginTransaction();// 开启第二个事务
  19. Person p3 = (Person) session2.get(Person.class, 1); // 获取p3对象
  20. System.out.println(p1 == p3); // 第二次比较
  21. Person p4 = (Person) session2.get(Person.class, 1); // 获取p4对象
  22. System.out.println(p3 == p4); // 第三次比较
  23. tx2.commit(); // 提交事务2
  24. session2.close(); // session2关闭
  25. }
  26. }

    5. 运行程序并查看结果

    使用 JUnit 测试运行 testCache() 方法,运行成功后,控制台的输出结果如图 2 所示。

27-Hibernate整合EHCache插件实现二级缓存 - 图2
图 2 testCache() 方法的测试结果

从图 2 中可以看到,控制台只输出了一个查询 SQL,这说明 Hibernate 只在数据库中查询了一次。而下面的 true、false 和 true 是三次比较输出的结果。详细解释具体如下。

1)在上述代码中,开启了两个 Session 和事务,从第一个 Session 中获取 p1 对象时,由于一级缓存和二级缓存中没有相应的数据,需要从数据库中查询,所以发出了 SQL 语句。

2)查询出 p1 对象后,p1 对象会保存到一级缓存和二级缓存中。当获取 p2 对象时,因为 Session 没有关闭,所以会从一级缓存中取出该对象。由于 p1 和 p2 对象都保存在一级缓存中,而且指向的是同一实体对象,所以第一次输出结果为true。

3)接着提交事务 tx1,并关闭 session1,此时一级缓存中的数据会被清除。

4)接下来开启第二个 Session 和事务,获取 p3 对象,此时控制台没有产生 SQL 语句是因为 p3 对象是从二级缓存中获取的。取出后,二级缓存会将数据同步到一级缓存中,这时 p3 对象又在一级缓存中存在了。

5)因为 p3 对象是从二级缓存中获取的,而二级缓存中存储的都是对象的散装数据,它们会重新 new 出一个新的对象,所以第二次输出的结果为 false。

6)最后获取 p4 对象时,由于一级缓存中已经存在了 Person 对象,Hibernate 会直接从一级缓存中获取,所以输出结果为 true。