Redis序列化存儲及日期格式的問題處理

Redis序列化存儲及日期格式

在模塊開發中,使用Redis做緩存是非常常見的技術,當我們註入RedisTempate模板時

redisTemplate.opsForValue().set("item_"+id,itemModel,10, TimeUnit.MINUTES);

key我們可以用固定開頭和商品id進行拼接,當然正常的項目開發中最好使用多級目錄進行分類,這裡隻做演示使用

可視化界面看到保存的數據是這樣的

在這裡插入圖片描述

這樣的數據是很不容易閱讀的,原因是Redis默認使用的是JAVA序列化方式,在序列化時使用瞭Redis協議中的編碼。

不過在這種痛苦的數據面前做調試等工作無疑是非常不舒服的

這時候就需要我們自定義序列化方式

@Configuration
public class RedisConfig {
    /**
     * 修改Redis默認的序列化方式,默認文件在RedisAutoConfiguration
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //設置key的序列化方式為string
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        //設置value的序列化方式為json
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //定制化關於時間格式序列化問題
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(DateTime.class,new JodaDateTimeJsonSerializer());
        simpleModule.addDeserializer(DateTime.class,new JodaDateTimeJsonDeSerializer());
        objectMapper.registerModule(simpleModule);
        //在保存結果中加入類信息,方便解析數據
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        return redisTemplate;
    }
}
public class JodaDateTimeJsonSerializer extends JsonSerializer<DateTime> {
    @Override
    public void serialize(DateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(value.toString("yyyy-MM-dd HH:mm:ss"));
    }
}
public class JodaDateTimeJsonDeSerializer extends JsonDeserializer<DateTime> {
    @Override
    public DateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String s = p.readValueAs(String.class);
        DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
        return DateTime.parse(s,dateTimeFormatter);
    }
}

在這裡插入圖片描述

Redis序列化LocalDateTime報錯

實體類日期字段使用LocalDateTime,在Redis序列化時報錯,會往Redis中寫入如下數據:

"createTime": {
  "date": {
    "year": 2019,
    "month": "MAY",
    "day": 15,
    "prolepticMonth": 24232,
    "era": [
      "java.time.chrono.IsoEra",
      "CE"
    ],
    "dayOfYear": 135,
    "dayOfWeek": "WEDNESDAY",
    "leapYear": false,
    "dayOfMonth": 15,
    "monthValue": 5,
    "chronology": {
      "id": "ISO",
      "calendarType": "iso8601"
    }
  },
  "time": {
    "hour": 11,
    "minute": 3,
    "second": 43,
    "nano": 758000000
  },
  "dayOfYear": 135,
  "dayOfWeek": "WEDNESDAY",
  "month": "MAY",
  "dayOfMonth": 15,
  "year": 2019,
  "monthValue": 5,
  "hour": 11,
  "minute": 3,
  "second": 43,
  "nano": 758000000,
  "chronology": [
    "java.time.chrono.IsoChronology",
    {
      "id": "ISO",
      "calendarType": "iso8601"
    }
  ]
}

方案一:實體類日期字段添加註解

每個LocalDateTime類型字段都需要添加,不建議使用

@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime birthday;

方案二:設置Redis對日期序列化處理

添加配置:

// 日期序列化處理
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new Jdk8Module())
  .registerModule(new JavaTimeModule())
  .registerModule(new ParameterNamesModule());

完整配置:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置連接工廠
        template.setConnectionFactory(factory);
        //使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值(默認使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修飾符范圍,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化輸入的類型,類必須是非final修飾的,final修飾的類,比如String,Integer等會跑出異常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);
        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        //使用StringRedisSerializer來序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        // 設置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();
        // 日期序列化處理
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om.registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule())
                .registerModule(new ParameterNamesModule());
        return template;
    }
}

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: