spring-boot-starter-data-redis,这个依赖是springboot通过lettuce链接redis,并且是mybatis存储的二级缓存存到redis的关键。
每个mapper包含多个sqlsession,每次sqlsession的更新操作都会影响到mapper的二级缓存
第一步:配置依赖
<!--redis lettuce-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 提供mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
第二部:配置配置文件
server:
port: 30000
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.1.139:3306/exam?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
redis:
host: 192.168.1.139
database: 0
port: 6379
timeout: 10s
logging:
level:
com:
lsz:
test:
mapper: debug
mybatis-plus:
configuration:
cache-enabled: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mapper/*.xml
第三部:配置reids序列化
package com.lsz.test.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* <b>Application name:</b> <br>
* <b>Application describing: </b> <br>
* <b>Date:</b> 2021/9/8 <br>
*
* @version V1.0
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 定义 key 的序列化方式为 string
// 需要注意这里Key使用了 StringRedisSerializer,那么Key只能是String类型的,不能为Long,Integer,否则会报错抛异常。
StringRedisSerializer redisSerializer = new StringRedisSerializer();
template.setKeySerializer(redisSerializer);
// 定义 value 的序列化方式为 json
@SuppressWarnings({"rawtypes", "unchecked"})
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash结构的key和value序列化方式
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setEnableTransactionSupport(true);
template.afterPropertiesSet();
//template.setConnectionFactory(factory);
return template;
}
}
第四部:配置获取redis模板的全局上下文
package com.lsz.test.config;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* <b>Application name:</b> <br>
* <b>Application describing: </b> <br>
* <b>Copyright:</b> Copyright © 2019 慧联世安 版权所有。<br>
* <b>Company:</b> 慧联世安 <br>
* <b>Date:</b> 2021/9/9 <br>
*
* @author <a href="mailto:946209979@qq.com">liushaozong</a>
* @version V1.0
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
ApplicationContextHolder.applicationContext = applicationContext; // NOSONAR
}
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
checkApplicationContext();
return applicationContext;
}
/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
checkApplicationContext();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
checkApplicationContext();
return (T) applicationContext.getBeansOfType(clazz);
}
/**
* 清除applicationContext静态变量.
*/
public static void cleanApplicationContext() {
applicationContext = null;
}
private static void checkApplicationContext() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
}
}
}
第五部:配置mapper和redis的内存关联
package com.lsz.test.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;
/**
* <b>Application name:</b> <br>
* <b>Application describing: </b> <br>
* <b>Copyright:</b> Copyright © 2019 慧联世安 版权所有。<br>
* <b>Company:</b> 慧联世安 <br>
* <b>Date:</b> 2021/9/8 <br>
*
* @author <a href="mailto:946209979@qq.com">liushaozong</a>
* @version V1.0
*/
@Slf4j
public class MybatisRedisCache implements Cache {
private String id;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间
public MybatisRedisCache(String id) {
this.id = id;
}
private RedisTemplate<Object, Object> getRedisTemplate(){
return ApplicationContextHolder.getBean("redisTemplate");
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.boundHashOps(getId()).put(key, value);
log.info("[结果放入到缓存中: " + key + "=" + value+" ]");
}
@Override
public Object getObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
Object value = redisTemplate.boundHashOps(getId()).get(key);
log.info("[从缓存中获取了: " + key + "=" + value+" ]");
return value;
}
@Override
public Object removeObject(Object key) {
RedisTemplate redisTemplate = getRedisTemplate();
Object value = redisTemplate.boundHashOps(getId()).delete(key);
log.info("[从缓存删除了: " + key + "=" + value+" ]");
return value;
}
@Override
public void clear() {
RedisTemplate redisTemplate = getRedisTemplate();
redisTemplate.delete(getId());
log.info("清空缓存!!!");
}
@Override
public int getSize() {
RedisTemplate redisTemplate = getRedisTemplate();
Long size = redisTemplate.boundHashOps(getId()).size();
return size == null ? 0 : size.intValue();
}
@Override
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
}
<?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 namespace="com.lsz.test.mapper.FireChemicalsUnitMapper">
<!-- 开启基于redis的二级缓存 -->
<cache type="com.lsz.test.config.MybatisRedisCache"/>
<cache/>
</mapper>