概述
Spring Cache抽象-基于XML的配置声明(基于EhCache的配置)
Spring Cache抽象-使用Java类注解的方式整合EhCache
EhCache概述
官方网站: http://www.ehcache.org/
Ehcache是一个用Java实现的使用简单,高速,实现线程安全的缓存管理类库,ehcache提供了用内存,磁盘文件存储,以及分布式存储方式等多种灵活的cache管理方案。
Ehcache 从 Hibernate 发展而来,逐渐涵盖了 Cahce 界的全部功能,是目前发展势头最好的一个项目。具有快速,简单,低消耗,依赖性小,扩展性强,支持对象或序列化缓存,支持缓存或元素的失效,提供 LRU、LFU 和 FIFO 缓存策略,支持内存缓存和磁盘缓存,分布式缓存机制等等特点。
特点
- 快速
- 简单
- 多种缓存策略
- 缓存数据有两级:内存和磁盘,因此无需担心容量问题
- 缓存数据会在虚拟机重启的过程中写入磁盘
- 可以通过 RMI、可插入 API 等方式进行分布式缓存
- 具有缓存和缓存管理器的侦听接口
- 支持多缓存管理器实例,以及一个实例的多个缓存区域
- 提供 Hibernate 的缓存实现
EhCache架构图
- CacheManager:是缓存管理器,可以通过单例或者多例的方式创建,Ehcache的入口类。
- Cache:每个CacheManager可以管理多个Cache,每个Cache可以采用hash的方式管理多个Element。
- Element:用于存放真正缓存内容的。
示例
1.添加mybatis-ehcache依赖
<properties> ..... <mybatis-ehcache.version>1.0.3</mybatis-ehcache.version> </properties> <dependencies> ...... <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>${mybatis-ehcache.version}</version> </dependency> </dependencies>
2. 配置EhCache
在src/main/resources 目录下 新建ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true"> <diskStore path="D:/cache" /> <defaultCache maxElementsInMemory="3000" eternal="false" copyOnRead="true" copyOnWrite="true" timeToIdleSeconds="3600" timeToLiveSeconds="3600" overflowToDisk="true" diskPersistent="true"/> </ehcache>
关于EhCache配置文件参考官网配置
http://www.ehcache.org/ehcache.xml
属性解读
copyOnRead:判断从缓存中读取数据时是返回对象的引用还是复制一个对象返回。 默认false,即返回数据的引用。 这种情况下返回的都是相同的对象,和MyBatis默认缓存中的只读对象是相同的。 如果设置为true,那就是可读可写缓存,每次读取缓存都会复制一个新的实例
copyOnWrite:判断写入缓存时是直接缓存对象的引用还是复制一个对象然后缓存。 默认也是false。如果想使用可读可写缓存,就需要将这两个属性配置为true。 如果使用只读缓存,可以不配置这两个属性,使用默认false即可。
3.修改PrivilegeMapper.xml中的缓存配置
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <!-- 当Mapper接口和XML文件关联的时候, namespace的值就需要配置成接口的全限定名称 --> <mapper namespace="com.artisan.mybatis.xml.mapper.PrivilegeMapper"> <!-- 在全局配置文件开启二级缓存的前提下,给Privilege开启二级缓存,使用默认配置 <cache/> --> <!-- 集成EhCache缓存 --> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> <resultMap id="privilegeMap" type="com.artisan.mybatis.xml.domain.SysPrivilege"> <id property="id" column="id" /> <result property="privilegeName" column="privilege_name" /> <result property="privilegeUrl" column="privilege_url" /> </resultMap> <select id="selectPrivilegeByIdWithCache" resultType="com.artisan.mybatis.xml.domain.SysPrivilege"> SELECT id, privilege_name privilegeName, privilege_url privilegeUrl FROM sys_privilege WHERE id = #{id} </select> </mapper>
e h c a c h e - c a c h e提供如下2个可选的缓存实现
并没有什么区别,都会输出缓存命中率的日志
只需要设置type属性就可以使用EhCache缓存了,这时候cache的其他属性都不会起作用,这对缓存的配置都在ehcache.xml中进行。
在ehcache.xml中只有一个默认的缓存配置,所以配置使用EhCache缓存的Mapper映射文件都会有一个以映射文件命令空间命名的缓存。 如果想针对某一个命名空间配置,需要在ehcache.xml中添加一个和映射文件命名空间一致的缓存配置。比如针对PrivilegeMapper
<cache name="com.artisan.mybatis.xml.mapper.PrivilegeMapper" maxElementsInMemory="3000" eternal="false" copyOnRead="true" copyOnWrite="true" timeToIdleSeconds="3600" timeToLiveSeconds="3600" overflowToDisk="true" diskPersistent="true"/>
4.单元测试
@Test public void selectPrivilegeByIdWithCacheTest() { logger.info("selectPrivilegeByIdWithCacheTest"); SqlSession sqlSession = getSqlSession(); SysPrivilege sysPrivilege = null; try { // 获取接口 PrivilegeMapper privilegeMapper = sqlSession.getMapper(PrivilegeMapper.class); // 调用接口方法 sysPrivilege = privilegeMapper.selectPrivilegeByIdWithCache(1L); sysPrivilege.setPrivilegeName("New Priv"); // 再次调用相同的接口方法,查询相同的用户 logger.info("再次调用相同的接口方法,查询相同的用户 Begin"); SysPrivilege sysPrivilege2 = privilegeMapper.selectPrivilegeByIdWithCache(1L); logger.info("再次调用相同的接口方法,查询相同的用户 End"); // 一级缓存在同一个sqlSession中,虽然没有更新数据库,但是会使用一级缓存 Assert.assertEquals("New Priv", sysPrivilege2.getPrivilegeName()); // sysPrivilege 和 sysPrivilege2 是同一个实例 Assert.assertEquals(sysPrivilege, sysPrivilege2); } finally { // sqlSession关闭后,在二级缓存开启的前提下,会写入二级缓存 sqlSession.close(); } logger.info("重新获取一个SqlSession"); sqlSession = getSqlSession(); try { // 获取接口 PrivilegeMapper privilegeMapper = sqlSession.getMapper(PrivilegeMapper.class); // 调用接口方法 SysPrivilege sysPrivilege2 = privilegeMapper.selectPrivilegeByIdWithCache(1L); sysPrivilege.setPrivilegeName("New Priv"); // 第二个session获取的权限名为 New Priv Assert.assertEquals("New Priv", sysPrivilege2.getPrivilegeName()); // 这里的sysPrivilege2 和 前一个session中的sysPrivilege不是同一个实例 Assert.assertNotEquals(sysPrivilege, sysPrivilege2); // 获取sysPrivilege3 SysPrivilege sysPrivilege3 = privilegeMapper.selectPrivilegeByIdWithCache(1L); // 这里的sysPrivilege2 和sysPrivilege3是两个不同的实例 Assert.assertNotEquals(sysPrivilege2, sysPrivilege3); } finally { // sqlSession关闭后,在二级缓存开启的前提下,会写入二级缓存 sqlSession.close(); } }
日志
2018-05-07 19:58:27,653 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-05-07 19:58:27,656 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-05-07 19:58:27,659 INFO [main] (PrivilegeMapperTest.java:38) - selectPrivilegeByIdWithCacheTest 2018-05-07 19:58:27,682 DEBUG [main] (LoggingCache.java:62) - Cache Hit Ratio [com.artisan.mybatis.xml.mapper.PrivilegeMapper]: 0.0 2018-05-07 19:58:27,743 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Preparing: SELECT id, privilege_name privilegeName, privilege_url privilegeUrl FROM sys_privilege WHERE id = ? 2018-05-07 19:58:27,855 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1(Long) 2018-05-07 19:58:27,886 TRACE [main] (BaseJdbcLogger.java:151) - <== Columns: id, privilegeName, privilegeUrl 2018-05-07 19:58:27,887 TRACE [main] (BaseJdbcLogger.java:151) - <== Row: 1, 用户管理, /users 2018-05-07 19:58:27,890 DEBUG [main] (BaseJdbcLogger.java:145) - <== Total: 1 2018-05-07 19:58:27,892 INFO [main] (PrivilegeMapperTest.java:48) - 再次调用相同的接口方法,查询相同的用户 Begin 2018-05-07 19:58:27,892 DEBUG [main] (LoggingCache.java:62) - Cache Hit Ratio [com.artisan.mybatis.xml.mapper.PrivilegeMapper]: 0.0 2018-05-07 19:58:27,893 INFO [main] (PrivilegeMapperTest.java:50) - 再次调用相同的接口方法,查询相同的用户 End 2018-05-07 19:58:27,923 INFO [main] (PrivilegeMapperTest.java:60) - 重新获取一个SqlSession 2018-05-07 19:58:27,927 DEBUG [main] (LoggingCache.java:62) - Cache Hit Ratio [com.artisan.mybatis.xml.mapper.PrivilegeMapper]: 0.3333333333333333 2018-05-07 19:58:27,928 DEBUG [main] (LoggingCache.java:62) - Cache Hit Ratio [com.artisan.mybatis.xml.mapper.PrivilegeMapper]: 0.5