配置 MyBatis 二级缓存
开启 MyBatis 二级缓存
在 Spring Boot 配置文件中开启 MyBatis 二级缓存,配置代码如下:
mybatis:
configuration:
cache-enabled: true
Redis 配置
spring:
redis:
lettuce:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
sentinel:
master: mymaster
nodes: 192.168.10.131:26379, 192.168.10.131:26380, 192.168.10.131:26381
实体类实现序列化接口并声明序列号
private static final long serialVersionUID = 8289770415244673535L;
IDEA 提示生成序列号
默认情况下 Intellij IDEA 不会提示继承了 Serializable 接口的类生成 serialVersionUID 的警告。如果需要生成 serialVersionUID,需要手动配置。
File -> Settings -> Inspections -> Serialization issues -> Serialization class without ‘serialVersionUID’
创建相关工具类
实现 Spring ApplicationContextAware 接口,用于手动注入 Bean
创建一个名为 ApplicationContextHolder 的工具类,代码如下:
package com.funtl.itoken.common.context;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextHolder implements ApplicationContextAware, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(ApplicationContextHolder.class);
private static ApplicationContext applicationContext;
/**
* 获取存储在静态变量中的 ApplicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量 applicationContext 中获取 Bean,自动转型成所赋值对象的类型
*
* @param name
* @param <T>
* @return
*/
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量 applicationContext 中获取 Bean,自动转型成所赋值对象的类型
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
assertContextInjected();
return applicationContext.getBean(clazz);
}
/**
* 实现 DisposableBean 接口,在 Context 关闭时清理静态变量
*
* @throws Exception
*/
public void destroy() throws Exception {
logger.debug("清除 SpringContext 中的 ApplicationContext: {}", applicationContext);
applicationContext = null;
}
/**
* 实现 ApplicationContextAware 接口,注入 Context 到静态变量中
*
* @param applicationContext
* @throws BeansException
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextHolder.applicationContext = applicationContext;
}
/**
* 断言 Context 已经注入
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicationContext 属性未注入,请在 spring-context.xml 配置中定义 ApplicationContextHolder");
}
}
实现 MyBatis Cache 接口,用于自定义缓存为 Redis
创建一个名为 RedisCache 的工具类,代码如下:
package com.funtl.itoken.common.utils;
import com.funtl.itoken.common.context.ApplicationContextHolder;
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Redis 缓存工具类
* <p>Title: RedisCache</p>
* <p>Description: </p>
*
* @author Lusifer
* @version 1.0.0
* @date 2018/8/13 6:03
*/
public class RedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final String id; // cache instance id
private RedisTemplate redisTemplate;
private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
@Override
public String getId() {
return id;
}
/**
* Put query result to redis
*
* @param key
* @param value
*/
@Override
public void putObject(Object key, Object value) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
logger.debug("Put query result to redis");
} catch (Throwable t) {
logger.error("Redis put failed", t);
}
}
/**
* Get cached query result from redis
*
* @param key
* @return
*/
@Override
public Object getObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
ValueOperations opsForValue = redisTemplate.opsForValue();
logger.debug("Get cached query result from redis");
// System.out.println("****" + opsForValue.get(key).toString());
return opsForValue.get(key);
} catch (Throwable t) {
logger.error("Redis get failed, fail over to db", t);
return null;
}
}
/**
* Remove cached query result from redis
*
* @param key
* @return
*/
@Override
@SuppressWarnings("unchecked")
public Object removeObject(Object key) {
try {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(key);
logger.debug("Remove cached query result from redis");
} catch (Throwable t) {
logger.error("Redis remove failed", t);
}
return null;
}
/**
* Clears this cache instance
*/
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.execute((RedisCallback) connection -> {
connection.flushDb();
return null;
});
logger.debug("Clear all the cached query result from redis");
}
/**
* This method is not used
*
* @return
*/
@Override
public int getSize() {
return 0;
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
private RedisTemplate getRedisTemplate() {
if (redisTemplate == null) {
redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
}
Mapper 接口中增加注解
在 Mapper 接口中增加注解,声明需要使用二级缓存
package com.funtl.itoken.common.mapper;
import com.funtl.itoken.common.domain.TbSysUser;
import com.funtl.itoken.common.utils.RedisCache;
import org.apache.ibatis.annotations.CacheNamespace;
import tk.mybatis.mapper.MyMapper;
@CacheNamespace(implementation = RedisCache.class)
public interface TbSysUserMapper extends MyMapper<TbSysUser> {
}