目录
一、入门demo
1、pom
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>plus-cache-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--尽量不要同时导入mybatis 和 mybatis_plus,避免版本差异-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--cache-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-redis-reactive</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-cache</artifactId>-->
<!-- </dependency>-->
<!--spring-boot-starter-data-redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
</project>
2、配置文件
server.port=1111
server.servlet.context-path=/plusDemo
#mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3308/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=wtyy
#mybatis
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
#打印日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#redis
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.password=
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=-1
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.time-between-eviction-runs=30s
#cache
#类型指定redis
spring.cache.type=redis
#一个小时,以毫秒为单位
spring.cache.redis.time-to-live=3600000
#给缓存的建都起一个前缀。 如果指定了前缀就用我们指定的,如果没有就默认使用缓存的名字作为前缀,一般不指定
#spring.cache.redis.key-prefix=CACHE_
#指定是否使用前缀
spring.cache.redis.use-key-prefix=true
#是否缓存空值,防止缓存穿透
spring.cache.redis.cache-null-values=true
3、config
package com.pluscache.demo.config;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisProperties.class)//开启属性绑定配置的功能
public class MyCacheConfig {
}
4、controller
package com.pluscache.demo.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pluscache.demo.dto.ParamDTO;
import com.pluscache.demo.dto.UserDTO;
import com.pluscache.demo.service.ParamService;
import com.pluscache.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/param")
public class ParamController {
@Autowired
private ParamService paramService;
@RequestMapping("/listParams")
public List<ParamDTO> listParams() {
return paramService.listParams();
}
@RequestMapping("/addParam")
public void addParam(ParamDTO paramDTO) {
paramService.addParam(paramDTO);
}
@RequestMapping("/listParamsByKey")
public List<ParamDTO> listParamsByKey(String key) {
return paramService.listParamsByKey(key);
}
@RequestMapping("/deleteById")
public void deleteById(String key) {
paramService.deleteByKey(key);
}
}
5、service
package com.pluscache.demo.service.impl;
import com.pluscache.demo.dto.ParamDTO;
import com.pluscache.demo.repository.ParamRepository;
import com.pluscache.demo.service.ParamService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("paramService")
public class ParamServiceImpl implements ParamService {
@Autowired
private ParamRepository paramRepository;
@Override
public List<ParamDTO> listParams() {
return paramRepository.listParams();
}
@Override
public void addParam(ParamDTO paramDTO) {
paramRepository.addParam(paramDTO);
}
@Override
public List<ParamDTO> listParamsByKey(String key) {
return paramRepository.listParamsByKey(key);
}
@Override
public void deleteByKey(String key) {
paramRepository.deleteByKey(key);
}
}
6、dao
package com.pluscache.demo.repository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pluscache.demo.constant.DbConstant;
import com.pluscache.demo.dto.ParamDTO;
import com.pluscache.demo.dto.UserDTO;
import com.pluscache.demo.mapper.ParamMapper;
import com.pluscache.demo.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
@CacheConfig(cacheNames = {DbConstant.TABLE_PARAM_NAME_KEY_FORMAT})
@Slf4j
public class ParamRepository {
@Autowired
private ParamMapper paramMapper;
//1、无参
@Cacheable(key = "#root.methodName")
public List<ParamDTO> listParams() {
log.info("listParams无参start");
LambdaQueryWrapper<ParamDTO> queryWrapper = new LambdaQueryWrapper<>();
return paramMapper.selectList(queryWrapper);
}
@CacheEvict(key = "#root.methodName")
public void addParam(ParamDTO paramDTO) {
paramMapper.insert(paramDTO);
}
//2、单参
@Cacheable(key = "{#p0}")
public List<ParamDTO> listParamsByKey(String key) {
log.info("listParamsByKey有参start");
LambdaQueryWrapper<ParamDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ParamDTO::getParamKey,key);
return paramMapper.selectList(queryWrapper);
}
@CacheEvict(key = "{#p0}",beforeInvocation = true)
public void deleteByKey(String key) {
LambdaQueryWrapper<ParamDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ParamDTO::getParamKey,key);
paramMapper.delete(queryWrapper);
//int a = 1/0;
}
}
package com.pluscache.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pluscache.demo.dto.ParamDTO;
import com.pluscache.demo.dto.UserDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ParamMapper extends BaseMapper<ParamDTO> {
void batchDeleteByKey(@Param("keys") List<String> keys);
}
7、dto与常量
package com.pluscache.demo.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("t_param")
public class ParamDTO implements Serializable {
private Integer id;
private String paramKey;
private String paramValue;
}
package com.pluscache.demo.constant;
public class DbConstant {
public static final String TABLE_PARAM_NAME_KEY_FORMAT = "cache:t_param:";
}
8、测试:
8.1、无参
① 第一次访问localhost:1111/plusDemo/param/listParams控制台输出:
2024-04-25T17:14:33.330+08:00 INFO 36136 --- [nio-1111-exec-2] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@644769222 wrapping com.mysql.cj.jdbc.ConnectionImpl@5ee5b9f5] will not be managed by Spring
==> Preparing: SELECT id,param_key,param_value FROM t_param
==> Parameters:
<== Columns: id, param_key, param_value
<== Row: 1, a, a_1
<== Row: 2, a, a_2
<== Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@14369ce6]
查看redis:
② 再次访问,返回数据和第一次一样,但是控制台无输出,断点可以看到在serveimImpl直接返回了,并没有进入ParamRepository。
8.2、单参
(1)缓存与删除缓存
访问localhost:1111/plusDemo/param/listParamsByKey?key=a
第一次走db查询,后面不走db查询。查看redis:
key为t_param:::a:
value为:
访问localhost:1111/plusDemo/param/deleteById?key=a 刷新redis可以看到缓存已经被删除了
这时再访问listParamsByKey发现又走db层了
(2)删除缓存加入异常
@CacheEvict(key = "{#p0}",beforeInvocation = true)
public void deleteByKey(String key) {
LambdaQueryWrapper<ParamDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ParamDTO::getParamKey,key);
paramMapper.delete(queryWrapper);
int a = 1/0;
}
访问localhost:1111/plusDemo/param/listParamsByKey?key=a后再访问localhost:1111/plusDemo/param/deleteById?key=a可以看到缓存已经清除了,重新走db查询
如果没有写beforeInvocation = true,仍然走缓存查询,验证beforeInvocation决定了缓存和db的先后顺序。
二、自定义删除key
对上面的demo进行改造:
① 如果Repository中有多种组合key的缓存,CUD接口在@CacheEvict注解中维护key比较麻烦,所以我们可以把key组合提到自定义Component中维护。
② 在前面也提到了,Repository的CUD接口应该接收(集合)对象,可以根据实体(集合)在自定义Component中获取所有key的组合;
而spring cache的@CacheEvict只支持String,为了支持集合操作,需要重写evict方法。
1、pom
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>cache-key-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--尽量不要同时导入mybatis 和 mybatis_plus,避免版本差异-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
</project>
2、配置文件
server.port=6666
server.servlet.context-path=/cacheKeyDemo
#mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3308/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=wtyy
#mybatis
mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
#打印SQL
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#redis
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.password=
spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.lettuce.pool.max-wait=-1
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.time-between-eviction-runs=30s
#cache
#本例重写了cacheManage,无需配置spring.cache,这里配置也无效
3、spring cache重写
(1)RedisCache
package com.pluscache.demo.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import java.util.List;
@Slf4j
public class MyRedisCache extends RedisCache {
protected MyRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfiguration) {
super(name, cacheWriter, cacheConfiguration);
}
@Override
public void evict(Object key) {
log.info("key为:{}",key.toString());
if(key instanceof List<?>) {
List<String> keys = (List<String>) key;
keys.forEach( item -> super.evict(item));
}else{
super.evict(key);
}
}
}
(2)RedisCacheManager
package com.pluscache.demo.config;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class MyRedisCacheManager extends RedisCacheManager {
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
public MyRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public MyRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public MyRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
/**
* 覆盖父类创建RedisCache,采用自定义的RedisCacheResolver
*/
@Override
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
return new MyRedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
@Override
public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(getCacheNames().size());
getCacheNames().forEach(it -> {
RedisCache cache = MyRedisCache.class.cast(lookupCache(it));
configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
});
return Collections.unmodifiableMap(configurationMap);
}
}
(3)CacheConfig
package com.pluscache.demo.config;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import java.time.Duration;
@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisProperties.class)//开启属性绑定配置的功能
public class CacheConfig {
private RedisCacheConfiguration defaultCacheConfig() {
return RedisCacheConfiguration.defaultCacheConfig()
//缓存key的前缀
//.computePrefixWith(versionCacheKeyPrefix)
// key过期时间,60分钟
.entryTtl(Duration.ofMinutes(60));
// .disableCachingNullValues()
// Serialization & Deserialization when using annotation
//.serializeKeysWith(STRING_PAIR)
// .serializeValuesWith(FAST_JSON_PAIR);
//.serializeValuesWith(PROTO_STUFF_PAIR);
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// 这里可以根据实际情况创建并返回RedisConnectionFactory的实现类,例如LettuceConnectionFactory或JedisConnectionFactory
return new LettuceConnectionFactory();
}
@Bean
public CacheManager cacheManager() {
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory());
return new MyRedisCacheManager(cacheWriter,defaultCacheConfig());
}
}
4、dto
package com.pluscache.demo.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import jakarta.validation.constraints.Max;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("t_user")
public class UserDTO implements Serializable {
private Integer id;
private String userName;
private String userAccount;
private Integer age;
}
5、service
package com.pluscache.demo.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pluscache.demo.dto.UserDTO;
import com.pluscache.demo.repository.UserRepository;
import com.pluscache.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
public UserRepository userRepository;
@Override
public List<UserDTO> listUsers() {
return userRepository.listUsers();
}
@Override
public List<UserDTO> listUsersByAccount(String userAccount) {
return userRepository.listUsersByAccount(userAccount);
}
@Override
public UserDTO getById(Integer id) {
return userRepository.getById(id);
}
@Override
public UserDTO getByIdAndAccount(Integer id, String userAccount) {
return userRepository.getByIdAndAccount(id,userAccount);
}
@Override
public void saveOrUpdate(UserDTO userDTO) {
userRepository.saveOrUpdate(userDTO);
}
@Override
public void updateBatch(List<UserDTO> userDTOs) {
userRepository.updateBatch(userDTOs);
}
@Override
public void deleteById(Integer id) {
UserDTO userDTO = userRepository.getById(id);
if (userDTO != null){
userRepository.deleteById(userDTO,id);
}
}
@Override
public void deleteByIds(List<Integer> ids) {
List<UserDTO> userDTOs = userRepository.listByIds(ids);
if(CollectionUtils.isEmpty(userDTOs)){
return;
}
userRepository.deleteByIds(userDTOs,ids);
}
}
6、dao
(1)repository
package com.pluscache.demo.repository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.pluscache.demo.dto.UserDTO;
import com.pluscache.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
@CacheConfig(cacheNames = "t_user")
public class UserRepository {
@Autowired
private UserMapper userMapper;
@Cacheable(key = "@userKeyConstructor.listAll()",
unless = "@userKeyConstructor.checkCacheSize(#result)")
public List<UserDTO> listUsers() {
LambdaQueryWrapper<UserDTO> queryWrapper = new LambdaQueryWrapper<>();
return userMapper.selectList(queryWrapper);
}
@Cacheable(key = "@userKeyConstructor.listByAccount(#userAccount)",
unless = "@userKeyConstructor.checkCacheSize(#result)")
public List<UserDTO> listUsersByAccount(String userAccount) {
LambdaQueryWrapper<UserDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserDTO::getUserAccount,userAccount);
return userMapper.selectList(queryWrapper);
}
@Cacheable(key = "@userKeyConstructor.getById(#id)",
unless = "@userKeyConstructor.checkCacheSize(#result)")
public UserDTO getById(Integer id) {
LambdaQueryWrapper<UserDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserDTO::getId,id);
return userMapper.selectOne(queryWrapper);
}
@Cacheable(key = "@userKeyConstructor.getByIdAccount(#id,#userAccount)",
unless = "@userKeyConstructor.checkCacheSize(#result)")
public UserDTO getByIdAndAccount(Integer id, String userAccount) {
LambdaQueryWrapper<UserDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(UserDTO::getUserAccount,userAccount);
queryWrapper.eq(UserDTO::getId,id);
return userMapper.selectOne(queryWrapper);
}
public List<UserDTO> listByIds(List<Integer> ids) {
LambdaQueryWrapper<UserDTO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(UserDTO::getId,ids);
return userMapper.selectList(queryWrapper);
}
@CacheEvict(key = "@userKeyConstructor.getAllKeys(#userDTO)")
public void saveOrUpdate(UserDTO userDTO) {
if(userDTO.getId() != null){
userMapper.updateById(userDTO);
return;
}
userMapper.insert(userDTO);
}
@CacheEvict(key = "@userKeyConstructor.getAllKeys(#userDTOs)")
public void updateBatch(List<UserDTO> userDTOs) {
userMapper.updateBatch(userDTOs);
}
@CacheEvict(key = "@userKeyConstructor.getAllKeys(#userDTO)")
public void deleteById(UserDTO userDTO, Integer id) {
userMapper.deleteById(id);
}
@CacheEvict(key = "@userKeyConstructor.getAllKeys(#userDTOs)")
public void deleteByIds(List<UserDTO> userDTOs, List<Integer> ids) {
userMapper.deleteBatchIds(ids);
}
}
(2)mapper
package com.pluscache.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pluscache.demo.dto.UserDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
//@Mapper
public interface UserMapper extends BaseMapper<UserDTO> {
void updateBatch(@Param("list")List<UserDTO> userDTOs);
}
(3)xml
<?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.pluscache.demo.mapper.UserMapper">
<update id="updateBatch">
<foreach collection="list" item="user" separator=";" index="index">
update t_user
<set>
age = #{user.userName},
user_account = #{user.userAccount}
</set>
<where>
id = #{user.id}
</where>
</foreach>
</update>
</mapper>
7、自定义key组合组件
package com.pluscache.demo.constructor;
import com.pluscache.demo.dto.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
@Component("userKeyConstructor")
@Slf4j
public class UserKeyConstructor {
private static final String LIST_ALL = "listAll";
private static final String GET_BY_ID = "getById,%s";
private static final String LIST_BY_ACCOUNT = "listByAccount,%s";
private static final String GET_BY_ID_ACCOUNT = "getByIdAccount,%s,%s";
public static List<String> getAllKeys(UserDTO userDTO){
return List.of(
LIST_ALL,
getById(userDTO.getId()),
listByAccount(userDTO.getUserAccount()),
getByIdAccount(userDTO.getId(),userDTO.getUserAccount())
);
}
public static List<String> getAllKeys(List<UserDTO> userDTOs){
List<String> keys = new ArrayList<>();
userDTOs.forEach(userDTO -> keys.addAll(getAllKeys(userDTO)));
return keys;
}
public static String getById(Integer id) {
return String.format(GET_BY_ID, id);
}
public static String listByAccount(String userAccount) {
return String.format(LIST_BY_ACCOUNT, userAccount);
}
public static String getByIdAccount(Integer id,String userAccount) {
return String.format(GET_BY_ID_ACCOUNT,id, userAccount);
}
public static String listAll() {
return LIST_ALL;
}
public boolean checkCacheSize(List<?> result) {
if (CollectionUtils.isEmpty(result)) {
return false;
}
if (result.size() > 50) {
log.debug("数据过多,不宜缓存");
return true;
}
return false;
}
}
8、controller
package com.pluscache.demo.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pluscache.demo.dto.UserDTO;
import com.pluscache.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/listUsers")
public List<UserDTO> listUsers() {
return userService.listUsers();
}
@RequestMapping("/listByAccount")
public List<UserDTO> listUsersByAccount(String userAccount) {
return userService.listUsersByAccount(userAccount);
}
@RequestMapping("/getById")
public UserDTO getById(Integer id) {
return userService.getById(id);
}
@RequestMapping("/getByIdAndAccount")
public UserDTO getByIdAndAccount(Integer id,String userAccount) {
return userService.getByIdAndAccount(id,userAccount);
}
@RequestMapping("/saveOrUpdate")
public void saveOrUpdate(UserDTO userDTO) {
userService.saveOrUpdate(userDTO);
}
@RequestMapping("/updateBatch")
public void updateBatch(@RequestBody List<UserDTO> userDTOs) {
userService.updateBatch(userDTOs);
}
@RequestMapping("/deleteById")
public void deleteById(Integer id) {
userService.deleteById(id);
}
@RequestMapping("/deleteByIds")
public void deleteByIds(@RequestBody List<Integer> ids) {
userService.deleteByIds(ids);
}
}
9、启动类
package com.pluscache.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.pluscache.demo.mapper")
@SpringBootApplication
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
10、测试
先初始化两条数据
(1)加缓存
① localhost:6666/cacheKeyDemo/user/listUsers
第一次访问,控制台打印:
查看redis
再次访问命中缓存,控制台无SQL输出。下面的查询同。
② localhost:6666/cacheKeyDemo/user/listByAccount?userAccount=zs
第一次访问,查看redis,缓存生成成功
③ localhost:6666/cacheKeyDemo/user/getById?id=1
第一次访问,查看redis,缓存生成成功
④ localhost:6666/cacheKeyDemo/user/getByIdAndAccount?userAccount=zs&id=1
第一次访问,查看redis,缓存生成成功
以上操作,再执行一次id=2、userAccount=lisi,这时缓存如下:
(2)saveOrUpdate清除缓存
在(1)的基础上,访问
localhost:6666/cacheKeyDemo/user/saveOrUpdate?userAccount=zs&id=1
查询redis,可以看到id=1、userAccount=zs的组合缓存成功删除
(3)updateBatch清除缓存
在(1)的基础上,
访问localhost:6666/cacheKeyDemo/user/updateBatch
查看redis,zs和lisi的缓存都删除了
(4)deleteById清除缓存
在(1)的基础上:
访问localhost:6666/cacheKeyDemo/user/deleteById?id=1
可以看到id=1对应的user数据,相关缓存都删除了:
(5)deleteByIds清除缓存
第4步把id=1的数据删除了,现在手动从数据库再创建一条
重新一次(1)
现在访问localhost:6666/cacheKeyDemo/user/deleteByIds
查看redis:id=1、2对应数据的相关缓存都删除了:
(6)验证原evict方法
如果使用原evict
@Override
public void evict(Object key) {
// if(key instanceof List<?>) {
// List<String> keys = (List<String>) key;
// keys.forEach( item -> super.evict(item));
// }else{
log.info("key为:{}",key.toString());
super.evict(key);
// }
}
访问localhost:6666/cacheKeyDemo/user/deleteById?id=1
从控制台打印可以看到将整个list做为key了
缓存自然没有被清除