spring-boot-data Redis 使用

发布时间 2023-06-09 18:37:49作者: ayiZzzz

spring-boot-data redis

Spring Boot 提供了 Redis 集成启动器(Starter),依赖于 spring-data-redis 和 lettuce 库。 spring-data-redis:对 Reids 底层开发包高度封装,让开发者对 Redis 的 CRUD 操作起来更加方便。

创建工程

  • 导入相关依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

引入 spring-boot-starter-data-redis 依赖就可以快速帮助我们操作 Redis

配置资源 resource/application.yml

  • 配置 Redis 连接源和 Lecture 相关信息
spring:
  datasource:
    url: jdbc:mysql://主机地址:3306/stu?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
  redis:
    host: 主机地址
    port: 端口号
    password: 密码
    lettuce:
      pool:
        max-active: 20 # 连接池最大连接数
        max-wait: 1 # 最大阻塞等待时间
        max-idle: 5 # 最大
        min-idle: 0 # 连接池的最小空闲连接
      shutdown-timeout: 100

配置 jackson

  • 配置查询转换异常 和SON转化为对象,会转化成Map类型相关问题
 @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 自定义类型转换器
        SimpleModule simpleModule = new SimpleModule();
        final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
        // 时间转换 module - 用户 LocalDateTime 日期类型的序列化和反序列化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 配置 LocalDateTime 的序列化规则和格式化规则
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
//        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
//        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        // 添加自定义序列化器
        simpleModule.addSerializer(Money.class , new MoneySerializer());
        simpleModule.addDeserializer(Money.class , new MoneyDeserializer());
        objectMapper.registerModule(simpleModule);
        objectMapper.registerModule(javaTimeModule);
        // 配置查询缓存转换异常
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 必须设置,否则无法将JSON转化为对象,会转化成Map类型
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
        return objectMapper;
    }
  • 配置序列化 Module 可以适配非基本类型的序列化如 LocalDateTimeorg.joa.Money

1、实现 class 的相关序列化和反序列化器
2、在相关 Module 进行序列化器的注册,如果没有相关的 Module 可以采用 SimpleModule 进行相关的序列化器和反序列化器的添加

3、序列化通过实现 StdSerializer 或者 JsonSerializer 进行自定义序列化的实现

// 标记成 JSON 的 Component
@JsonComponent 
public class MoneySerializer extends StdSerializer<Money> {
    public MoneySerializer() {
        super(Money.class);
    }

    @Override
    public void serialize(Money money, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeNumber(money.getAmountMinor());
    }
}

4、反序列化通过实现 MoneyDeserializer 或者 JsonDeserializer 进行自定义反序列化的实现

@JsonComponent
public class MoneyDeserializer extends StdDeserializer<Money> {
    public MoneyDeserializer() {
        super(Money.class);
    }

    @Override
    public Money deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return Money.ofMinor(CurrencyUnit.of("CNY"), p.getLongValue());
    }
}

5、序列化会对 Money 的内容进行相关的改变

实现自定义序列化器

  • 通过实现 RedisSerializer 进行自定义 Redis 序列化器的实现,同时实现 InitializingBean 接口适配 Spring
public class RedisKeySerializer implements InitializingBean, RedisSerializer<String> {

    private final String keyPrefix = "test:";

    private final String charsetName = "UTF-8";

    private Charset charset;

    @Override
    public byte[] serialize(String key) throws SerializationException {
        String builderKey = keyPrefix + key;
        return builderKey.getBytes();
    }

    @Override
    public String deserialize(byte[] bytes) throws SerializationException {
        return new String(bytes, charset);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        charset = Charset.forName(charsetName);
    }
}
  • 注意:实现 InitializingBean 会让 RedisKeySerializer 在注册到 Spring 容器时初始化相关的 Charset 字符集

配置 RedisConfig

1、为了解决 RedisTemplate<K , V> 在操作时的序列化器问题,需要把 setKeySerializer 设置成 RedisKeySerializer 如果不设置会把 K 进行序列化后在进行相关的存储操作,此时 K 就是 K 序列化后的值
2、配置 Redis 序列化器添加之前我们配置的 ObjectMapper 对象从而解决 1、配置查询缓存转换异常;2、JSON转化为对象,会转化成Map类型 两个问题

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        // 创建模板
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 设置序列化工具
//
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//        // key和 hashKey采用 string序列化
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        redisTemplate.setKeySerializer(new RedisKeySerializer());
        redisTemplate.setHashKeySerializer(new RedisKeySerializer());
//        // value和 hashValue采用 JSON序列化

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
  • StringRedisTemplate
    @Bean(name = "stringStringRedisTemplate")
    public StringRedisTemplate stringStringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();

        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        stringRedisTemplate.setKeySerializer(new RedisKeySerializer());
        stringRedisTemplate.setHashKeySerializer(new RedisKeySerializer());
        stringRedisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        stringRedisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        stringRedisTemplate.afterPropertiesSet();
        return stringRedisTemplate;
    }

注意事项

1、StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
2、RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
3、两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。
4、操作简单的数据可以采用 StringRedisTemplate 可以减少序列化的开销,操作复杂的数据类型时可以采用 RedisTemplate
5、StringRedisTemplate 存储非基本类型时需要把 class 转换成 JSONString

  User user = new User();

        user.setAge(20);
        user.setName("231213");
        user.setDateTime(LocalDateTime.now());

        String key = StrUtil.builder().append(RedisConstants.USER_PREFIX).append(user.getName()).toString();
        stringRedisTemplate.opsForValue().set(key ,JSONUtil.toJsonStr(user));

6、获取时如果获取的是非基本类型数据需要通过 FASTJSON2 的 JSON.parseObject(vaue , clazz); 进行对象转换