springboot 2.X+redis+springcache 之 数据缓存

Redis简介

Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。 

第一步:
pom.xml文件:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

第二步:
application.properties文件:

spring.redis.database=0
# redis server host
spring.redis.host=127.0.0.1
# redis password
spring.redis.password=
# connection port
spring.redis.port=6379
# time out
spring.redis.timeout=10000
# 配置缓存过期时间
spring.redis.expiretime=1800

# pool settings:
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1

# set Key prefix.
# spring.cache.redis.key-prefix=dev
# Entry expiration in milliseconds. By default the entries never expire.
spring.cache.redis.time-to-live=1d
# Whether to use the key prefix when writing to Redis.
spring.cache.redis.use-key-prefix=true

第三步:

编写RedisConfig文件:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
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.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;
import java.time.Duration;

//@EnableCaching 【重点】不能加,不然@Cacheable无效
@Configuration
//加载该前缀的配置信息,提供set方法即可自动注入
@ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisConfig extends CachingConfigurerSupport{

    private static final Logger log = LoggerFactory.getLogger(RedisConfig.class);

    private Duration timeToLive = Duration.ZERO;
    public void setTimeToLive(Duration timeToLive) {
        this.timeToLive = timeToLive;
    }

    /**
     * 自定义生成redis-key
     *
     * @return
     */
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName()).append(".");
                sb.append(method.getName()).append(".");
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                log.info("------> 自定义生成redis-key完成,keyGenerator=" + sb.toString());
                return sb.toString();
            }
        };
    }

    /* @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        //【重点】【new RedisCacheManager()在 springboot2.x 里无效】
        RedisCacheManager manager = new RedisCacheManager(redisTemplate);
        manager.setUsePrefix(true);
        RedisCachePrefix cachePrefix = new RedisPrefix("prefix");
        manager.setCachePrefix(cachePrefix);
        // 整体缓存过期时间
        manager.setDefaultExpiration(3600L);
        // 设置缓存过期时间。key和缓存过期时间,单位秒
        Map expiresMap = new HashMap<>();
        expiresMap.put("user", 1000L);
        manager.setExpires(expiresMap);
        return manager;
    }*/

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(this.timeToLive)
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
                .disableCachingNullValues();

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();

        log.info("------> 自定义RedisCacheManager加载完成");
        return redisCacheManager;
    }

    @Bean(name = "redisTemplate")
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(keySerializer());
        redisTemplate.setValueSerializer(valueSerializer());
        redisTemplate.setHashValueSerializer(valueSerializer());

        log.info("------> 自定义RedisTemplate加载完成");
        return redisTemplate;
    }

    private RedisSerializer keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer valueSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
} 
  

springboot2.X RedisCacheManager已经没有了单参数的构造方法 ,参考文档

第四步:

注解需要缓存的地方


@Service("UserService")
//对应ehcache.xml文件里面的
@CacheConfig(cacheNames = "user")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    //无论怎样,都将方法的返回结果放到缓存当中。
    //ehcache3 会自动处理要缓存的key-value里面的key
    @CachePut
    public void save(User user) {
        System.out.println("新增功能,更新缓存,直接写库, id=" + user);
        userRepository.save(user);
    }

    @Override
    @CachePut
    public void update(User user) {
        System.out.println("更新功能,更新缓存,直接写库, id=" + user);
        userRepository.save(user);
    }

    @Override
    //将一条或者多条数据从缓存中删除。
    @CacheEvict
    public void remove(Long userId) throws Exception {
        System.out.println("删除功能,删除缓存,直接写库, id=" + userId);
        userRepository.deleteById(userId);
    }

    @Override
    @CacheEvict
    public void batchRemove(Long[] userIds) throws Exception {
        for (Long userId : userIds) {
            userRepository.deleteById(userId);
        }
    }

    @Override
    public List getUserList() {
        return userRepository.findAll();
    }

    @Override
    public User findByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    @Override
    //在方法执行前Spring先是否有缓存数据,如果有直接返回。如果没有数据,调用方法并将方法返回值存放在缓存当中。
    @Cacheable
    public User findById(long id) {
        //可以在这里打一个断点,发现第二次查询不会被断点卡到为配置成功
        System.out.println("查询功能,缓存找不到,直接读库, id=" + id);
        return userRepository.findById(id);
    }
}

第五步:

配置启动文件:

@SpringBootApplication
@EnableCaching//开启缓存
@PropertySource("application.properties")
public class XXXApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(XXXApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(XXXApplication.class, args);
    }
}

可能出现的问题:

1、 类型转换出现问题:看看是不是工程里面加入了热部署,注释掉热部署即可

java.lang.ClassCastException:
com.XXX.system.UserDO cannot be cast to com.XXX.system.UserDO

2、这里涉及redis的常见操作:

查看key的类型
type key
查看所有key
keys *
删除一个key
DEL key
清空一个DB
FLUSHDB
清空所有
FLUSHALL
切换到其他DB
select 1

可能的问题:

WRONGTYPE Operation against a key holding the wrong kind of value

原因是:类型不一致,使用redis命令不当造成的。

例如:

127.0.0.1:6379> keys *
1) "user~keys"
2) "user_1"
127.0.0.1:6379> get user~keys
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> type user~keys
zset
127.0.0.1:6379> zrange user~keys 0 10
1) "user_1"

参考文档:

https://blog.csdn.net/guokezhongdeyuzhou/article/details/79789629
https://blog.csdn.net/Mirt_/article/details/80934312
https://blog.csdn.net/xieliaowa9231/article/details/78995465

你可能感兴趣的:(缓存,SpringBoot,springboot学习之路)