解决RedisTemplate的key默认序列化器的问题

网友投稿 674 2022-11-01


解决RedisTemplate的key默认序列化器的问题

redis的客户端换成了spring-boot-starter-data-redis,碰到了一个奇怪的问题,

在同一个方法中

1.先hset,再hget,正常获得数据。

在不同的方法中 先hset,再hget获取不到数据,通过redis的monitor监控发现了命令的问题:

实际我的key为JK_HASH:csrk,hashkey为user,但是根据上图所示,实际执行的命令多了好多其他字符,这是什么原因呢?

在服务器端先确认发现实际有这个Hash,通过hset可以得到正确的数据,所以第一次执行hset的时候命令是正常的,问题可能出现在hget上面,先打开源码看一下

@SuppressWarnings("unchecked")

public HV get(K key, Object hashKey) {

final byte[] rawKey = rawKey(key);

final byte[] rawHashKey = rawHashKey(hashKey);

byte[] rawHashValue = execute(new RedisCallback() {

public byte[] doInRedis(RedisConnection connection) {

return connection.hGet(rawKey, rawHashKey);

}

}, true);

return (HV) deserializeHashValue(rawHashValue);

}

从这里可以看到实际上传给redis的都是byte数据,而byte数组是rawKey和rawHashKey生成的,先看下rawKey方法

@SuppressWarnings("unchecked")

byte[] rawKey(Object key) {

Assert.notNull(key, "non null key required");

if (keySerializer() == null && key instanceof byte[]) {

return (byte[]) key;

}

return keySerializer().serialize(key);

}

然后进一步跟踪keySerializer()方法

RedisSerializer keySerializer() {

return template.getKeySerializer();

}

public RedisSerializer> getKeySerializer() {

return keySerializer;

}

最后跟踪到是RedisTemplate中的属性keySerializer导致的,而通过打印keySerializer的class发现 默认使用的是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer,但它是如何进行初始化的呢,默认的构造函数中并没有对该属性进行初始化。

根据RedisTemplate的类关系发现它是继承RedisAccessor的,而此类是实现的org.springframework.beans.factory.InitializingBean接口,这个接口有个特性,凡是继承该接口的类,在初始化bean的时候会执行afterPropertiesSet方法。

而afterPropertiesSet方法中,确实对keySerializer进行了初始化:

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(this);

}

initialized = true;

}

在这里可以看到默认使用的正是org.springframework.data.redis.serializer.JdkSerializationRedisSerializer,而问题正在这里,通过查询可以发现序列化器有这些,而在这里我们需要使用的是StringRedisSerializer

加入如下代码:

@Autowired(required = false)

public void setReyYTfYkgcudisTemplate(RedisTemplate redisTemplate) {

RedisSerializer stringSerializer = new StringRedisSerializer();

redisTemplate.setKeySerializer(stringSerializer);

redisTemplate.setValueSerializer(stringSerializer);

redisTemplate.setHashKeySerializer(stringSerializer);

redisTemplate.setHashValueSerializer(stringSerializer);

this.redisTemplate = redisTemplate;

}

重新进行测试,方法1hset,方法2hget,方法2能拿到正确的数据,完毕。

补充:redisTemplate取对象时反序列化失败

错误:

org.springframework.data.redis.serializer.SerializationException: Could not read jsON: Unrecognized field

注意:反序列化时要保证实体对象有一个无参构造函数,否则反序列化也会失败

json序列化是根据set和get方法来序列化字段的,如果方法正好有set和get开头但没有对应field,那么反序列化就会失败。

可以在实体类加上注解@JsonIgnoreProperties(ignoreUnknown = true)忽略实体中没有对应的json的key值,或者在set方法上加上@JsonIgnore注解,如果是用mongoDb数据库,第二种方法那么就不大适用了,总不能去ObjectId类操作,这是修改别人的源代码。

以下是redisTemplate在springboot2.x里面存取对象的配置,等同于第一种方法

@Configuration

@EnableCaching

public class RedisConfig extends CachingConfigurerSupport {

@Bean

public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {

RedisTemplate redisTemplate = new RedisTemplate<>();

redisTemplate.setConnectionFactory(connectionFactory);

//Use Jackson 2Json RedisSerializer to serialize and deserialize the value of redis (default JDK serialization)

Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

ObjectMapper objectMapper = new ObjectMapper();

objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

//将类名称序列化到json串中,去掉会导致得出来的的是LinkedHashMap对象,直接转换实体对象会失败

objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

//设置输入时忽略JSON字符串中存在而java对象实际没有的属性

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

//Use String RedisSerializer to serialize and deserialize the key value of redis

RedisSerializer redisSerializer = new StringRedisSerializehttp://r();

//key

redisTemplate.setKeySerializer(redisSerializer);

redisTemplate.setHashKeySerializer(redisSerializer);

//value

redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

redisTemplate.afterPropertiesSet();

return redisTemplate;

}

}

application.properties文件添加如下,springboot2.x连接池用的是lettuce

spring.redis.host=localhost

spring.redis.password=

# 连接超时时间(毫秒)

spring.redis.timeout=3000ms

# Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0

spring.redis.database=0

# 连接池最大连接数(使用负值表示没有限制) 默认 8

spring.redis.lettuce.pool.max-active=8

# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1

spring.redis.lettuce.pool.max-wait=-1

# 连接池中的最大空闲连接 默认 8

spring.redis.lettuce.pool.max-idle=8

# 连接池中的最小空闲连接 默认 0

spring.redis.lettuce.pool.min-idle=0

添加数据进redis

@Test

public void testRedis() {

Headset headset = new Headset();

redisTemplate.opsForValue().set("test:123", headset);

System.out.println(redisTemplate.opsForValue().get("test:123"));

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:UVA 437 The Tower of Babylon——dp
下一篇:UVA 12563 Jin Ge Jin Qu hao——01背包
相关文章

 发表评论

暂时没有评论,来抢沙发吧~