谈谈spring-data-redis遇到的问题

谈谈spring-data-redis遇到的问题

1. key序列化问题

使用spring-data-redis中的redisTemplate存储key-value,然后使用redis-cli去查询时查询不到相应的key。使用keys *时发现redis中key的前缀多了一些字符

\xac\xed\x00\x05t\x00\x0e

问题关键

使用spring-data-redis,默认情况下使用的是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer来进行序列化key

我们看看关键代码 RedisTemplate

public void afterPropertiesSet() {

        super.afterPropertiesSet();

        boolean defaultUsed = false;

        if (defaultSerializer == null) {

            defaultSerializer = new JdkSerializationRedisSerializer(
                    classLoader != null ? classLoader : this.getClass().getClassLoader());
        }

        if (enableDefaultSerializer) {

            if (keySerializer == null) {
                keySerializer = defaultSerializer;
                defaultUsed = true;
            }
            if (valueSerializer == null) {
                valueSerializer = defaultSerializer;
                defaultUsed = true;
            }
            if (hashKeySerializer == null) {
                hashKeySerializer = defaultSerializer;
                defaultUsed = true;
            }
            if (hashValueSerializer == null) {
                hashValueSerializer = defaultSerializer;
                defaultUsed = true;
            }
        }

        if (enableDefaultSerializer && defaultUsed) {
            Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
        }

        if (scriptExecutor == null) {
            this.scriptExecutor = new DefaultScriptExecutor<K>(this);
        }

        initialized = true;
    }

解决问题

手动设置key的序列化方式为StringRedisSerializer

看代码

@Bean
public RedisTemplate<String, ?> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
    redisTemplate.setConnectionFactory(factory);
    RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringRedisSerializer);
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    return redisTemplate;
}

2. value 序列化问题

我们在使用无参构造GenericJackson2JsonRedisSerializer序列化对象时,序列化出来的值是这样的

{
  "@class": "com.xxx.MenuTask",
  "taskId": "1617776640",
  "planId": "10000000",
  "createTime": [
    "java.util.Date",
    1500540438240
  ],
  "execDate": [
    "java.util.Date",
    1500480000000
  ],
  "status": 0,
  "totals": {
    "@class": "java.util.HashMap",
    "35430d2a56ae508f24a76a68e71b9f53": 1500,
    "75aa86c5a85f65687c95444655c51de9": 500,
    "ab90efa7a0079b88f224a296b211c209": 1000
  }
}

这样的json格式使用其他的json序列化时会出现无法解析的错误,如fastjson解析时会抛出异常

com.alibaba.fastjson.JSONException: parse error
    at com.alibaba.fastjson.serializer.DateCodec.cast(DateCodec.java:206)
    at com.alibaba.fastjson.parser.deserializer.AbstractDateDeserializer.deserialze(AbstractDateDeserializer.java:142)
    at com.alibaba.fastjson.parser.deserializer.AbstractDateDeserializer.deserialze(AbstractDateDeserializer.java:19)
    at com.alibaba.fastjson.parser.deserializer.DefaultFieldDeserializer.parseField(DefaultFieldDeserializer.java:71)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField(JavaBeanDeserializer.java:790)
    at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:595)

主要原因是因为Date格式不正确,应该是一个long类型的数字,而不是一个数组

问题关键

我们来分析一下源码

public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Object> {

    private final ObjectMapper mapper;

    /**
     * Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing.
     */
    public GenericJackson2JsonRedisSerializer() {
        this((String) null);
    }

    public GenericJackson2JsonRedisSerializer(String classPropertyTypeName) {

        this(new ObjectMapper());
//------------------------ start ------------------------
        mapper.registerModule(new SimpleModule().addSerializer(new NullValueSerializer(classPropertyTypeName)));

        if (StringUtils.hasText(classPropertyTypeName)) {
            mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName);
        } else {
            mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
        }
//------------------------ end ------------------------
    }

    public GenericJackson2JsonRedisSerializer(ObjectMapper mapper) {
        Assert.notNull(mapper, "ObjectMapper must not be null!");
        this.mapper = mapper;
    }
    ...
    ...
}

原因所在地方 // — start — // — end —之间的那段代码 源码的63-69行,这里会将属性的类型写到json中。

解决问题

手动设置一个ObjectMapper,这样redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(new ObjectMapper()));

@Bean
public RedisTemplate<String, ?> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
    redisTemplate.setConnectionFactory(factory);
    RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringRedisSerializer);
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(new ObjectMapper()));
    return redisTemplate;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值