Redis 序列化 GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializer的区别

 /**
     * 实例化 RedisTemplate 对象
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> functionDomainRedisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key 都使用String 序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

GenericJackson2JsonRedisSerializer

GenericJackson2JsonRedisSerializer序列化时,会保存序列化的对象的完全限定名,从redis获取数据时即可直接反序列化成指定的对象

通用参数如下:

List<Student> students = new ArrayList<>();
Student xm = new Student("小明", 12);
Student ph = new Student("胖虎", 12);
students.add(xm);
students.add(ph);

(1)VALUE 存普通对象

存:

  Student xm = new Student("小明", 12);
  redisTemplate.opsForValue().set(xm.getName(), xm);

image-20211228210957187

值中附带了实体信息

取:

Student xmCache = (Student)redisTemplate.opsForValue().get(xm.getName());
// Student(name=小明, age=12)
System.out.println(xmCache);

结论:GenericJackson2JsonRedisSerializer 序列化方式 可直接存储VALUE为对象,且从Redis获取可进行强转

(2)VALUE 存对象集合

存:

redisTemplate.opsForSet().add("students-genericJackson2JsonRedisSerializer", students);
redisTemplate.opsForValue().set("students-str", students);

image-20211228212005299

值中既有实体类完全限定名又有集合类型

image-20211228212643057

具体值中携带了实体类完全限定名

取:

List<Student> studentCache = (List<Student>) redisTemplate.opsForValue().get("students-str");
Set<Object> members = redisTemplate.opsForSet().members("students-genericJackson2JsonRedisSerializer");
// [Student(name=小明, age=12), Student(name=胖虎, age=12)]
System.out.println(studentCache);
// [Student(name=胖虎, age=12), Student(name=小明, age=12)]
System.out.println(members);

结论:GenericJackson2JsonRedisSerializer 序列化方式 可直接存储VALUE为对象集合,且从Redis获取可进行强转

(3)VALUE 存JSON字符串

redisTemplate.opsForValue().set(ph.getName(), JSON.toJSONString(ph));

存:

image-20211228221227065

无@class字段,且jSON字符串中含有转义符

取:

Object o = redisTemplate.opsForValue().get(ph.getName());
System.out.println(o);

image-20211229101706340

注意点:因为这里存是JSON字符串,所以取数据时候,也无法直接强制转为Student对象,我们需要手动调用方法,将JSON字符串转换为JAVA对象

redisTemplate.opsForValue().set(ph.getName(), JSON.toJSONString(ph));
Object o = redisTemplate.opsForValue().get(ph.getName());
// Student(name=胖虎, age=12)
System.out.println(o == null ? null : JSON.parseObject(o.toString(), Student.class));

结论:GenericJackson2JsonRedisSerializer 序列化方式 可直接存储VALUE为JSON字符串形式,且从Redis获取后不可进行强转,需要将结果转为字符串,再将JSON字符串转为自己需要的对象或对象集合

(4)管道Pipelined存数据

存:

List<Student> students = new ArrayList<>();
Student xm = new Student("小明", 12); Student ph = new Student("胖虎", 12);
students.add(xm); students.add(ph);
redisTemplate.executePipelined((RedisCallback) conn -> {
    students.forEach(s -> conn.set(s.getName().getBytes(), JSON.toJSONString(s).getBytes()));
    return null;
});

image-20211228214432318

image-20211228214442601

管道操作的数据并没有@class 实体类属性,实体类完全限定名丢失,那么这个时候还能否获取数据呢?

答案是不能

取:

取完直接强转:

Student xmCache = (Student)redisTemplate.opsForValue().get(xm.getName());
System.out.println(xmCache);

image-20211229100818157

GET取完不强转

Object xmCache = redisTemplate.opsForValue().get(xm.getName());
System.out.println(xmCache);

image-20211229101413242

MGET 批量获取

List<Object> objectList = redisTemplate.opsForValue().multiGet(students.stream().map(Student::getName).collect(Collectors.toList()));
System.out.println(objectList);
return objectList;

image-20211228214948224

ERROR 16400 — [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property ‘@class’
at [Source: (byte[])“{“age”:12,“name”:“小明”}”; line: 1, column: 26]; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property ‘@class’
at [Source: (byte[])“{“age”:12,“name”:“小明”}”; line: 1, column: 26]] with root cause

翻译过来的意思是:无法读取 JSON:尝试解析 [简单类型,类 java.lang.Object] 的子类型时缺少类型 ID:缺少类型 id 属性“@class”

结论: 使用GenericJackson2JsonRedisSerializer 序列化进行管道操作后,实体类@class属性会丢失,再次从redis获取数据将无法成功反序列化

Jackson2JsonRedisSerializer

通用参数如下:

List<Student> students = new ArrayList<>();
Student ys = new Student("亚索", 12);
Student mw = new Student("蛮王", 12);
students.add(ys);
students.add(mw);

(1)VALUE 存普通对象

存:

redisTemplate.opsForValue().set(ys.getName(), ys);
redisTemplate.opsForValue().set(mw.getName(), mw);

image-20211228221916282

取:

Student ysCache = (Student)redisTemplate.opsForValue().get(ys.getName());
System.out.println(ysCache);

发现报错:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.leilei.entity.Student

image-20211228222041663

断点调试:获取的ysCache 实际是一个LinkedHashMap

Object ysCache = redisTemplate.opsForValue().get(ys.getName());
System.out.println(ysCache);

image-20211228224537237

存对象集合结果一样…

redisTemplate.opsForValue().set("lol", students);
Object lolCache = redisTemplate.opsForValue().get("lol");
System.out.println(lolCache);

image-20211229102900203

解决办法:

Jackson2JsonRedisSerializer序列化时 存数据都采用JSON字符串方式,然后取数据时也不直接将结果强转,而是使用JSON反序列化 将JSON字符串反序列化为java对象或集合或者配置ObjectMapper使其支持对象序列化到Redis

在手动构造RedisTemplate时,为Jackson2JsonRedisSerializer设置ObjectMapper

    @Bean
    public <T> RedisTemplate<String, T> functionDomainRedisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // 如果直接使用Jackson2JsonRedisSerializer 获取存储的对象则会变为LinkedHashMap,添加ObjectMapper可解决
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

image-20221009213517730

image-20221009213450665

结论:Jackson2JsonRedisSerializer序列化方式,如VALUE直接存对象或者对象集合,获取时结果为LinkedHashMap,无法直接使用JDK强转方法转为对象或对象集合,需借助ObjectMapper实现对象直接序列化存储到Redis,从Redis读取反序列化对象

(2)VALUE 存JSON字符串

存:

redisTemplate.opsForValue().set(ys.getName(), JSON.toJSONString(ys));
redisTemplate.opsForValue().set(mw.getName(), JSON.toJSONString(mw));

image-20211228222523391

image-20211228222652367

字符串中有转义字符

取:

Object ysCache = redisTemplate.opsForValue().get(ys.getName());
// Student(name=亚索, age=122)
System.out.println(ysCache == null ? null : JSON.parseObject(ysCache.toString(), Student.class));
List<Object> cacheList = redisTemplate.opsForValue().multiGet(students.stream().map(Student::getName).collect(Collectors.toList()));
List<Student> studentList = cacheList.stream().filter(Objects::nonNull).map(Object::toString).map(x -> JSON.parseObject(x, Student.class)).collect(Collectors.toList());
// [Student(name=亚索, age=122), Student(name=蛮王, age=234)]
System.out.println(studentList);

结论:Jackson2JsonRedisSerializer序列化方式,VALUE存JSON字符串时,取值也为JSON字符串,需借助JSON工具进行转换为实体类或实体集合

(3)管道Pipelined存数据

存:

redisTemplate.executePipelined((RedisCallback<String>) conn -> {
    students.forEach(s -> conn.set(s.getName().getBytes(), JSON.toJSONString(s).getBytes()));
    return null;
});

image-20211228223227825

image-20211228223245401

取:

获取管道存储的值变为了LinkedHashMap

image-20211228224739230

因为我们需要将map转为java对象,我这里采用的是先转JSON字符串方式

image-20211228225015045

image-20211228225040706

List<Object> cacheList = redisTemplate.opsForValue().multiGet(students.stream().map(Student::getName).collect(Collectors.toList()));
if (CollectionUtils.isEmpty(cacheList)) {
    return null;
}
List<Student> studentList = cacheList.stream().filter(Objects::nonNull).map(JSON::toJSONString).map(x -> JSON.parseObject(x, Student.class)).collect(Collectors.toList());
// [Student(name=亚索, age=122), Student(name=蛮王, age=234)]
System.out.println(studentList);

结论:Jackson2JsonRedisSerializer 序列化方式,且使用管道操作后,获取的值也是一个linkedHashMap,需借助工具才可将值反序列化为实体对象或实体对象集合

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值