spring cache(三)demo

目录

一、入门demo

1、pom

2、配置文件

3、config

4、controller

5、service

6、dao

 7、dto与常量

8、测试:

8.1、无参

8.2、单参

(1)缓存与删除缓存

(2)删除缓存加入异常

二、自定义删除key

1、pom

2、配置文件

3、spring cache重写

(1)RedisCache 

(2)RedisCacheManager 

(3)CacheConfig 

4、dto

5、service

6、dao

(1)repository

(2)mapper

(3)xml 

7、自定义key组合组件

8、controller

9、启动类

10、测试

(1)加缓存

(2)saveOrUpdate清除缓存

(3)updateBatch清除缓存

(4)deleteById清除缓存

(5)deleteByIds清除缓存

(6)验证原evict方法


一、入门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了

缓存自然没有被清除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值