MyBatis 使用 J2Cache 作为二级缓存实现

大纲

J2Cache 的 Gradle 配置

在项目中引入以下依赖后,需要根据项目具体的运行状况调整 Gradle 的依赖配置,以下配置使用到 SpringBoot。

1
2
3
compile 'net.oschina.j2cache:j2cache-core:2.7.6-release'
compile 'net.oschina.j2cache:j2cache-mybatis:2.7.0-release'
compile 'net.oschina.j2cache:j2cache-spring-boot2-starter:2.7.6-release'

MyBatis 使用 J2Cache

首先在 MyBatis 的全局配置文件中开启二级缓存,然后根据下面的两种情况进行配置(二选一)

1
2
3
4
<settings>
<!-- 开启二级缓存,默认false -->
<setting name="cacheEnabled" value="true"/>
</settings>

第一种使用情况:对当前 namespace 内的所有 SQL 启用二级缓存

1
2
3
4
<mapper namespace="com.example.dao.user.UserApiMapper">
<!-- 指定MyBatis的二级缓存实现类 -->
<cache type="net.oschina.j2cache.mybatis.J2CacheAdapter"/>
</mapper>

或者使用 Java 注解进行配置,此写法会导致 XML 映射文件中的其他 SQL 无法使用二级缓存,下面会详细介绍

1
2
3
4
@CacheNamespace(implementation = J2CacheAdapter.class)
public interface UserApiMapper extends BaseMapper<UserApi> {

}

第二种使用情况:对当前 namespace 内指定的 SQL 设置是否启用二级缓存

1
2
3
4
5
6
7
8
9
10
<mapper namespace="com.example.dao.user.UserApiMapper">
<!-- 指定MyBatis的二级缓存实现类 -->
<cache type="net.oschina.j2cache.mybatis.J2CacheAdapter"/>

<!-- 通过配置useCache属性,指定当前sql是否启用二级缓存,默认为true,如果设置为false,则每次都会发sql去数据库查询 -->
<select id="selectRecord" resultMap="userApiMap" useCache="false">
select * from man_user_api
where userid = #{userId} and exchange_id = #{exchangeId}
</select>
</mapper>

或者使用 Java 注解进行配置,此写法会导致 XML 映射文件中的其他 SQL 无法使用二级缓存,下面会详细介绍

1
2
3
4
5
6
7
8
@CacheNamespace(implementation = J2CacheAdapter.class)
public interface UserApiMapper extends BaseMapper<UserApi> {

@Options(useCache = false)
@Select("select * from man_user_api where userid = #{userId} and exchange_id = #{exchangeId}")
public UserApi selectRecord(@Param(("userId")) long userId, @Param("exchangeId") long exchangeId);

}

J2Cache 的使用注意事项

MyBatis-Plus 二级缓存不生效

官方推荐使用的方式是统一在 XML 映射文件中配置缓存与 SQL,此方式在 MyBatis + MyBatis-Plus(version <= 2.0.9)的环境下可以正常工作。但是当使用版本号大于 2.0.9 的 MyBatis-Plus,使用 XML 配置缓存的方式会导致 MyBatis-Plus 的二级缓存不生效,具体表现为发出的 SQL(由 MyBatis-Plus 自动生成的 SQL)不能正常使用二级缓存。根据 MyBatis-Plus 官方文档的说明,当使用 2.0.9 以上的版本,需要在代码中 MyBatis 的 Mapper 层添加缓存注释 @CacheNamespace,并声明 implementation 属性的值为 Cache 接口的实现类,此时 XML 映射文件中不能再声明 cache 标签,代码如下:

1
2
3
4
@CacheNamespace(implementation = J2CacheAdapter.class)
public interface UserApiMapper extends BaseMapper<UserApi> {

}

MyBatis 二级缓存不生效

使用 @CacheNamespace 注解后,MyBatis-Plus 的二级缓存确实是生效了,但在 XML 映射文件中的其他 SQL 此时不能正常使用二级缓存,而在 Mapper 接口里通过注解声明的 SQL 则不受影响。

二级缓存使用总结

不管通过注解还是 XML 的方式配置缓存,总结大概有以下几种使用情况。实际开发中可以根据不同的业务需求混合使用不同的配置方式。第一种方式适合 SQL 比较复杂的业务场景,第三种方式适合 SQL 比较简单的业务场景,推荐使用第一种方式。

  • 第一种:MyBatis 单独正常使用二级缓存,将所有 SQL、缓存配置都写在 XML 映射文件中,此时 MyBatis-Plus 的二级缓存失效;
  • 第二种:MyBatis-Plus 正常使用二级缓存,在 Mapper 接口里声明注解 @CacheNamespace,此时 MyBatis 的二级缓存失效(针对 XML 中定义的 SQL),而 MyBatis 通过 Java 注解定义的 SQL 则依然会生效;
  • 第三种:MyBatis 与 MyBatis-Plus 同时正常使用二级缓存,将所有 SQL、缓存配置通过注解的方式定义在 Mapper 层;此时 XML 映射文件中不能再定义 SQL,否则 XML 中的 SQL 无法使用二级缓存。

J2Cache 配置文件的使用

J2Cache 支持使用 Redis 或者 RabbitMQ 的 Pub/Sub 服务,支持使用 Ehcache、Caffeine 作为一级缓存,支持使用 Jedis、Lettuce 作为 Redis 的客户端。目前 J2Cache 最主要的配置内容只能写在 j2cache.properties 文件中,如果将配置内容全部移动到 SpringBoot 的 xxx.yml 文件中,SpringBoot 应用的启动与单元测试都会出错,这与 J2Cache 的源码有关。

基于 SpringBoot 的单元测试类

当 MyBatis 的 Mapper 使用 J2Cache 作为二级缓存的实现,那么单元测试类中暂时需要指定 J2Cache 的相关配置,否则测试类无法正常启动。如果每个测试类都要加上一堆 J2Cache 的配置内容,实在是不方便,而且 J2Cache 的配置一旦更改,则需要修改每个测试类的代码。为了解决这种情况,可以在模块下创建 BaseSpringBootTest 基础测试类,然后其他测试类直接继承基础测试类即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 基础测试类
*/
@RunWith(SpringRunner.class)
@SpringBootTest(properties = {
"j2cache.l2-cache-open=true",
"j2cache.open-spring-cache=true",
"j2cache.allow-null-values=true",
"spring.cache.type=GENERIC",
"j2cache.cache-clean-mode=passive",
"j2cache.config-location=classpath:j2cache.properties"})
public class BaseSpringBootTest {

protected Logger logger = LoggerFactory.getLogger(BaseSpringBootTest.class);

}

/**
* 具体的测试类实现
*/
public class J2CacheMyBatisTest extends BaseSpringBootTest {

@Autowired
private UserApiMapper apiMapper;

private Logger logger = LoggerFactory.getLogger(J2CacheMyBatisTest.class);

@Test
public void util() {
UserApi api = apiMapper.selectById(1L);
logger.info("===> " + api);
}
}

临时关闭 MyBatis 使用 J2Cache

在 MyBatis 的全局配置文件中,关闭二级缓存即可,如下所示:

1
2
3
4
<settings>
<!-- 关闭二级缓存,默认false -->
<setting name="cacheEnabled" value="false"/>
</settings>

指定 J2Cache 的缓存大小与有效时间

当 J2Cache 作为 MyBatis 的二级缓存实现,J2Cache 官方支持在配置文件中指定缓存的大小与有效时间,具体配置如下:

1
2
3
4
5
6
7
8
9
10
# redis storage mode (generic|hash)
lettuce.storage = generic

# Enable/Disable ttl in redis cache data (if disabled, the object in redis will never expire, default:true)
# NOTICE: redis hash mode (redis.storage = hash) do not support this feature)
j2cache.sync_ttl_to_redis = true

# ttl for one level cache
# [name] = size, xxxx[s|m|h|d]
caffeine.region.default = 1000, 30m

重点关注的配置是 caffeine.region.default,上面配置了默认 Region(区域)的一级缓存大小为 1000,有效时间为 30 分钟。举个例子,如果需要为 UserAPI 表的记录单独设置缓存大小和有效时间,可以参考以下配置;其中 Region 是 Mapper 的类全名,大小为 2000,有效时间为 12 小时。当 J2Cache 找不到对应的 Region,默认会采用 caffeine.region.default 的配置策略,如下所示:

1
caffeine.region.com.example.dao.user.UserApiMapper = 2000, 12h