SpringBoot+Mybatis使用Redis作为二级缓存实现与解决删除/修改/新增无法更新缓存【调用clear方法】的问题。
一. 搭建环境
- 配置Maven。
- SpringBoot整合Redis。
- SpringBoot整合Mybatis。
父pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.5.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhao</groupId>
<artifactId>Redis</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<properties>
<jedis.version>4.0.0-beta4</jedis.version>
<junit.version>4.13.1</junit.version>
<spring.boot.starter.data.redis>2.5.4</spring.boot.starter.data.redis>
<lombok.version>1.18.20</lombok.version>
<mybatis.spring.boot.starter>2.2.0</mybatis.spring.boot.starter>
<druid.spring.boot.starter>1.2.5</druid.spring.boot.starter>
<mysql.connect.java>8.0.26</mysql.connect.java>
</properties>
<modules>
<module>01-Jedis</module>
<module>02-SpringBoot-Redis</module>
<module>03-Mybatis-Redis</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring.boot.starter.data.redis}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.starter}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.spring.boot.starter}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connect.java}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
配置文件application.yaml如下:
server:
port: 8809
spring:
redis:
database: 0
port: 6379
host: localhost
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
url: jdbc:mysql://localhost:3306/demo?characterEncoding=UTF-8
username: root
password: 123
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.zhao.entity
configuration:
cache-enabled: true
logging:
level:
com:
zhao:
dao: debug
二.获取Redis客户端的工具类
这里使用的是RedisTemplate作为操作Redis的客户端。
package com.zhao.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
//获取SpringBoot创建好的工厂
@Configuration
@Slf4j
public class ApplicationContextUtils implements ApplicationContextAware {
//保留下来的工厂
private static ApplicationContext applicationContext;
//将创建好的工厂以参数形式传递给这个类
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//提供在工厂中获取对象的方法
//RedisTemplate redisTemplate
public static Object getBean(String beanName) {
if (beanName == null && beanName.equals(" ")) {
return null;
}
return applicationContext.getBean(beanName);
}
}
三. 自定义Redis缓存类,实现Cache接口。
注意点: 1.必须要有一个字符串成员变量。2.必须要有一个构造方法为这个成员变量传值。3.pojo/entity对象必须实现序列化接口。
package com.zhao.cache;
import com.zhao.util.ApplicationContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.concurrent.locks.ReadWriteLock;
@Slf4j
public class RedisCache implements Cache {
private final String id;
public RedisCache(String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
//使用redis中的hash类型作为缓存的存储类型
getRedisTemplate().opsForHash().put(id.toString(), key.toString(), value);
log.info("查询结果存入缓存:id:{} key:{} value:{}", id.toString(), key.toString(), value.toString());
}
@Override
public Object getObject(Object key) {
//获取存储的数据
Object value = getRedisTemplate().opsForHash().get(id.toString(), key.toString());
log.info("取到缓存内容:{}", value);
return value;
}
@Override
public Object removeObject(Object key) {
log.info("执行removeObject方法移除缓存。");
getRedisTemplate().opsForHash().delete(id, key.toString());
return null;
}
@Override
public void clear() {
log.info("执行clear方法清空缓存。");
getRedisTemplate().delete(id.toString());//清空缓存
}
@Override
public int getSize() {
//获取hash中的key-value的数量
int num = getRedisTemplate().opsForHash().size(id).intValue();
log.info("获取缓存中键值对的数量:", num);
return num;
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
//封装redisTemplate
private RedisTemplate getRedisTemplate() {
//获取redisTemplate
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
- getId(): 获得Id值。这个Id值与namespace相同。
- putObject(Object key, Object value): 将查询出来的值存入缓存。由于键和值均是对象我们选用redisTemplate作为操作Redis的客户端,采用hash类型的数据存储缓存的值。key对应成员变量id、hkey对应方法参数 Object key、hvalue对应方法参数Object value。
- getObject(Object key): 根据key获取value。
- removeObject(Object key): Mybatis中的保留方法,只有在回退的时候才会调用,可以不予实现。
- clear(): update/delete/insert更新缓存时底层调用的是这个方法。这里我实现的是根据成员变量id来删除当前命名空间下的缓存。
- getReadWriteLock(): 读写锁来保持同步,可以不予实现。
- getRedisTemplate(): 这是我们自己添加的方法,不是Cache接口中的方法。通过使用该方法获取ApplicationContext中的RedisTemplate对象。此外key和hkey不需要存储对象,仅存储字符串就可以。所以我设置了key和hkey使用StringRedisSerializer,不使用默认的JDK序列化器。
四.开启二级缓存,设置缓存
这里使用xml方式
<!--开启mybatis的二级缓存-->
<cache type="com.zhao.cache.RedisCache"/>
五.特别注意点
如果在server层中添加了事务控制,如果在更新/删除/新增类型的方法上添加了@Transactional(propagation = Propagation.SUPPORTS)
或者@Transactional(propagation = Propagation.NOT_SUPPORTED)
注解,那么更新/删除/新增不会调用缓冲类中的clear()方法,Redis中的缓存也无法清除。