SpringBoot整合Redis之@Cacheable、@CachePut、@CacheEvict注解的使用

本文操作数据库,使用的是mybatis-plus,关于环境搭建,可查看博文
https://blog.csdn.net/qq_41712271/article/details/115756865

1 添加maven依赖

        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
        
            org.springframework.boot
            spring-boot-starter-cache
        
        
        
            org.apache.commons
            commons-pool2
            2.6.2
        

2 配置redis的RedisConfig类

package cn.jiqistudy.redis_1.Config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.time.Duration;

/**
 * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
 * 开启缓存配置,设置序列化
 */
@Configuration
@EnableCaching
public class RedisConfig {

    @Primary
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration
                //设置缓存的默认超时时间:30分钟
                .entryTtl(Duration.ofMinutes(30L))
                //如果是空值,不缓存
                .disableCachingNullValues()

                //设置key序列化器
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                //设置value序列化器
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));

        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration)
                .build();
    }

    /**
     * key序列化器
     */
    private RedisSerializer keySerializer() {
        return new StringRedisSerializer();
    }
    /**
     * value序列化器
     */
    private RedisSerializer valueSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
} 
  

3 业务处理编写,缓存操作的核心类

package cn.jiqistudy.redis_1.service;
import cn.jiqistudy.redis_1.mapper.UserMapper;
import cn.jiqistudy.redis_1.pojo.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
@CacheConfig(cacheNames = { "user" })
public class UserService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);

    @Resource
    private UserMapper userMapper;

    //新增缓存,注意方法的返回值
    @Cacheable(key="#id")
    public User findUserById(Integer id){
        return this.userMapper.selectById(id);
    }

    //更新缓存,注意方法的返回值
    @CachePut(key = "#obj.id")
    public User updateUser(User obj){
        this.userMapper.updateById(obj);
        return this.userMapper.selectById(obj.getId());
    }

    //删除缓存
    @CacheEvict(key = "#id")
    public void deleteUser(Integer id){
        QueryWrapper userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("id",id);
        this.userMapper.delete(userQueryWrapper);
    }
}

4 测试类编写

import cn.jiqistudy.redis_1.Redis1Application;
import cn.jiqistudy.redis_1.pojo.User;
import cn.jiqistudy.redis_1.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Redis1Application.class)
public class Test_7 {

    @Autowired
    private UserService userService;

    //将查询的结果,缓存起来
    @Test
    public void fangfa1()
    {
        User userById = userService.findUserById(2);
        System.out.println(userById);
    }

    //同时更新数据库和缓存
    @Test
    public void fangfa2()
    {
        User user = new User(2,"李盛霖","男1",15,"地球");
        userService.updateUser(user);
    }

    //同时删除数据库记录和缓存
    @Test
    public void fangfa3()
    {
        userService.deleteUser(1);
    }
}

详细说明

#### @CacheConfig

@CacheConfig是类级别的注解,统一该类的所有缓存前缀。
``` 
@CacheConfig(cacheNames = { "user" })
public class UserService {
```
以上代码,代表了该类的所有缓存可以都是"user::"为前缀

#### @Cacheable
@Cacheable是方法级别的注解,用于将方法的结果缓存起来
``` 
@Cacheable(key="#id")
public User findUserById(Integer id){
    return this.userMapper.selectByPrimaryKey(id);
}
```
以上方法被调用时,先从缓存中读取数据,如果缓存没有找到数据,再执行方法体,最后把返回值添加到缓存中。

注意:@Cacheable 一般是配合@CacheConfig一起使用的
例如上文的@CacheConfig(cacheNames = { "user" }) 和 @Cacheable(key="#id")一起使用时。
调用方法传入id=100,那redis对应的key=user::100 ,value通过采用GenericJackson2JsonRedisSerializer序列化为json
调用方法传入id=200,那redis对应的key=user::200 ,value通过采用GenericJackson2JsonRedisSerializer序列化为json

#### @CachePut
@CachePut是方法级别的注解,用于更新缓存
``` 
@CachePut(key = "#obj.id")
public User updateUser(User obj){
    this.userMapper.updateByPrimaryKeySelective(obj);
    return this.userMapper.selectByPrimaryKey(obj.getId());
}
```
以上方法被调用时,先执行方法体,然后springcache通过返回值更新缓存,即key = "#obj.id",value=User

#### @CacheEvict(key = "#id")
@CachePut是方法级别的注解,用于删除缓存
``` 
public void deleteUser(Integer id){
    User user=new User();
    user.setId(id);
    user.setDeleted((byte)1);
    this.userMapper.updateByPrimaryKeySelective(user);
}
```
以上方法被调用时,先执行方法体,在通过方法参数删除缓存


### springcache的大坑

1. 对于redis的缓存,springcache只支持String,其他的Hash 、List、set、ZSet都不支持,
   所以对于Hash 、List、set、ZSet只能用RedisTemplate
   
2. 对于多表查询的数据缓存,springcache是不支持的,只支持单表的简单缓存
   对于多表的整体缓存,只能用RedisTemplate。