近期在开发中遇到了这样一个问题,起初为了方便起见,使用StringRedisTemplate来操作redis,后来采用RedisTemplate去取值的时候,发现无法拿到值,具体的业务场景我用一个案例来描述。
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
stringRedisTemplate.opsForValue().set("1","1");
String s1 = stringRedisTemplate.opsForValue().get("1");
System.out.println(s1); //输出 1
redisTemplate.opsForValue().set("2","2");
String s2 = (String) redisTemplate.opsForValue().get("2");
System.out.println(s2); //输出 2
stringRedisTemplate.opsForValue().set("3","3");
String s3 = (String) redisTemplate.opsForValue().get("3");
System.out.println(s3); //输出 null
redisTemplate.opsForValue().set("4","4");
String s4 = stringRedisTemplate.opsForValue().get("4");
System.out.println(s4); //输出 null
}
读者可以简单的尝试一下。
通过问题可以看出,redisTemplate只能操作redisTemplate对象所存储的数据,stringRedisTemplate只能操作stringRedisTemplate对象所存储的数据。
首先可以确定的是,我们连接的是同一台redis,也就排除了数据存储位置导致问题的可能性。
于是,我们直接来查询redis中是否存在这四个数据
我们发现,1和3是可以查询出来的,但是2和4却显示查询不到,换句话说,stringRedisTemplate对象操作的数据是可以查询到的,但是redisTemplate对象操作的数据无法查到。
但是,为什么我们在程序中可以查询到2这个值呢?
唯一能解释的是:
这个“2”虽然存在,但是已经不是“2”了!
我们通过 keys * 命令来显示所有键
发现“1”,“3”存在,但是“2”,“4”已经变成了"\xac\xed\x00\x05t\x00\x012"和"\xac\xed\x00\x05t\x00\x014"
问题逐渐浮出了水面:当我们使用stringRedisTemplate对象来操作数据的时候,存入redis时并没有转码,但是在使用redisTemplate对象操作数据时,存入redis时将原数据转码了!
接下来我们来看看StringRedisTemplate的构造和RedisTemplate的构造有什么不同。
由于StringRedisTemplate继承了RedisTemplate,所以我们先看RedisTemplate。
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
······
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
······
@Override
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;
}
从上述代码中可以看出,RedisTemplate在序列化时采用的是JdkSerializationRedisSerializer,即JDK提供的序列化方式,但是如果我们设置范型为String,则会使用UTF-8
我们再来看StringRedisTemplate的构造
public class StringRedisTemplate extends RedisTemplate<String, String> {
/**
* Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)}
* and {@link #afterPropertiesSet()} still need to be called.
*/
public StringRedisTemplate() {
setKeySerializer(RedisSerializer.string());
setValueSerializer(RedisSerializer.string());
setHashKeySerializer(RedisSerializer.string());
setHashValueSerializer(RedisSerializer.string());
}
···
}
static RedisSerializer<String> string() {
return StringRedisSerializer.UTF_8;
}
可以看到,在构造方法中将所有的序列化方式都使用了UTF-8
所以,如果我们想要用redisTemplate对象去操作stringRedisTemplate的数据,我们可以将redisTemplate的序列化方式也转成UTF-8
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//新增代码,将redisTemplate的key和value以UTF-8序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.string());
//下面的代码保持不变
stringRedisTemplate.opsForValue().set("1", "1");
String s1 = stringRedisTemplate.opsForValue().get("1");
System.out.println(s1); // 1
redisTemplate.opsForValue().set("2", "2");
String s2 = (String) redisTemplate.opsForValue().get("2");
System.out.println(s2); // 2
stringRedisTemplate.opsForValue().set("3", "3");
String s3 = (String) redisTemplate.opsForValue().get("3");
System.out.println(s3); // 3
redisTemplate.opsForValue().set("4", "4");
String s4 = stringRedisTemplate.opsForValue().get("4");
System.out.println(s4); //4
}
打印结果