解决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<byte[]>() {
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) {
if (keySerializer() == null && key instanceof byte[]) {
return (byte[]) key;
}
return keySerializer().serialize(key);
}
然后进⼀步跟踪keySerializer()⽅法
RedisSerializer keySerializer() {
KeySerializer();
}
public RedisSerializer<?> getKeySerializer() {
return keySerializer;
}
最后跟踪到是RedisTemplate中的属性keySerializer导致的,⽽通过打印keySerializer的class发现默认使⽤的是
org.dis.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 : Class().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) {
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<K>(this);
}
initialized = true;
}
在这⾥可以看到默认使⽤的正是org.dis.serializer.JdkSerializationRedisSerializ
er,⽽问题正在这⾥,通过查询可以发现序列化器有这些,⽽在这⾥我们需要使⽤的是StringRedisSerializer
加⼊如下代码:
@Autowired(required = false)
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);spring framework是什么框架的
redisTemplate.setHashValueSerializer(stringSerializer);
}
重新进⾏测试,⽅法1hset,⽅法2hget,⽅法2能拿到正确的数据,完毕。
补充:redisTemplate取对象时反序列化失败
错误:
org.dis.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<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> 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对象,直接转换实体对象会失败
/
/设置输⼊时忽略JSON字符串中存在⽽Java对象实际没有的属性
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//Use String RedisSerializer to serialize and deserialize the key value of redis
RedisSerializer redisSerializer = new StringRedisSerializer();
//key
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
//value
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
application.properties⽂件添加如下,springboot2.x连接池⽤的是lettuce
# 连接超时时间(毫秒)
# Redis默认情况下有16个分⽚,这⾥配置具体使⽤的分⽚,默认是0
# 连接池最⼤连接数(使⽤负值表⽰没有限制)默认 8
# 连接池最⼤阻塞等待时间(使⽤负值表⽰没有限制)默认 -1
# 连接池中的最⼤空闲连接默认 8
# 连接池中的最⼩空闲连接默认 0
添加数据进redis
@Test
public void testRedis() {
Headset headset = new Headset();
redisTemplate.opsForValue().set("test:123", headset);
System.out.println(redisTemplate.opsForValue().get("test:123"));
}
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。如有错误或未考虑完全的地⽅,望不吝赐教。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论