在SpringBoot项目中用Redis做Mybatis的二级缓存。
1、添加redis依赖
<!--redis缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、在application.yml添加redis配置
spring:
redis:
database: 0
host: 10.18.28.225
port: 6379
password: gswwjzfp
jedis:
pool:
max-active: 8
min-idle: 0
timeout: 600000m
3、必须开启mybatis的二级缓存
#mybatis配置
mybatis:
#指定实体所在的包名
typeAliasesPackage: com.gsww.scjzfp.open.entity
#指定mapper.xml文件位置
mapperLocations: classpath:mapper/*.xml
configuration:
#开启驼峰命名
map-underscore-to-camel-case: true
#开启二级缓存
cache-enabled: true
4、实体必须实现序列化接口
public class test implements Serializable {
private static final long serialVersionUID = 1770242665698288333L;
5、编写Redis配置类RedisConfig。重写了Redis序列化的方式,改用Json的数据结构传输数据。配置RedisTemplate并定义Serializer方式
@Configuration
public class RedisConfig {
/**
* 注入 RedisConnectionFactory
*/
@Autowired
RedisConnectionFactory redisConnectionFactory;
/**
* redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 设置键(key)和值(value)的序列化采用Jackson2JsonRedisSerializer。
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
6、编写类ApplicationContextHolder。实现Spring的ApplicationContextAware来获取ApplicationContext,从中获取容器的bean
@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");
}
}
7、编写RedisCache类。Mybatis二级缓存默认使用的是其他的缓存,这里需要实现Cache接口来自定义一个缓存类去实现二级缓存。
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");
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;
}
}
8、在mapper.xml文件中引入自定义好的缓存
<!--eviction:定义缓存移除机制(算法),默认为LRU(最近最少使用),它会清除最少使用的数据,还有一种FIFO(先进先出),它会清除最先进来的数据。
flushInterval:定义缓存刷新周期,单位为毫秒。
size:标识缓存cache中容纳的最大元素,默认为1024。
readOnly:默认为false,可配置为true缓存只读。
-->
<cache type="com.atguigu.utils.RedisCache">
<property name="eviction" value="LRU" />
<property name="flushInterval" value="6000000" />
<property name="size" value="1024" />
<property name="readOnly" value="false" />
</cache>