当前位置: 首页 > article >正文

Redis中储存含LocalDateTime属性对象的序列化实现

目录

1.问题1 向Redis中存入序列化对象

1.1引入 :

1.2解决方案:

1.2.1首先引入依赖

1.2.2然后在RedisConfig中进行配置

1.3 介绍下ObjectMapper

1.3.1  ObjectMapper

1.3.2  objectMapper.registerModule(new JavaTimeModule());

1.3.3  GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

2 问题2 从Redis取出数据转换为Java对象

2.1 引入:

2.2 解决方案

3 序列化优点

4 objectMapper.convertValue()

4.1 原理

4.2 基本步骤

4.3 示例


1.问题1 向Redis中存入序列化对象

"Could not write JSON: Java 8 date/time type `java.time.LocalDateTime` not supported by default"

1.1引入 :

        在mysql中存储时间除了TimeStamp以外,常用的就是就是DateTime了,若在表中包含DateTime字段,对应的Java实体类属性常使用LocalDateTime类。

        LocalDateTime(包括LocalDate等常见类)Java 8 中引入的新时间和日期 API 中的类,属于 java.time 包,它提供了更强大的日期时间操作功能。Java 8 之前的 java.util.Date 和 java.util.Calendar 类在设计上有许多局限性,而 Java 8 通过引入 java.time 包,提供了更加直观和线程安全的日期时间处理类。

但需要注意:

        当类中包含 Java 8 的 LocalDateTime 类型属性时,无法直接将该类序列化为 JSON 并存储在 Redis 中,因为 LocalDateTime 是非标准的时间格式,JSON 序列化工具(例如 Jackson 或 Gson)无法直接处理这种类型。

若直接进行存储会出现如下错误:

        "Could not write JSON: Java 8 date/time type `java.time.LocalDateTime` not supported by default......"

Could not write JSON: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.hspedu.seckill.pojo.User["registerDate"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.hspedu.seckill.pojo.User["registerDate"]) 

1.2解决方案:

1.2.1首先引入依赖

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.12.3</version> <!-- 请根据你项目的 Jackson 版本选择合适的版本号 -->
</dependency>
1.2.2然后在RedisConfig中进行配置
@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // 设置Key序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        // 设置HashKey序列化方式 
        template.setHashKeySerializer(new StringRedisSerializer());

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化Redis的value
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

        // 设置Value序列化方式
        template.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置HashValue序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
        }

    }

完成配置后即可在直接将包含LocalDateTime属性的对象直接进行序列化存储在Redis中,

如redisTemplate.opsForValue().set(key,yourObject);的方式直接存储即可。

1.3 介绍下ObjectMapper

1.3.1  ObjectMapper

上面代码中配置 RedisTemplate 的序列化机制,以便能够序列化和反序列化包含 LocalDateTime 等 Java 8 时间类型的对象。我们详细分解下这几行代码的功能:

 ObjectMapper objectMapper = new ObjectMapper();

作用ObjectMapper 是 Jackson 库的核心类,用来将 Java 对象转换为 JSON 字符串,或者将 JSON 字符串解析为 Java 对象。

Jackson 是一个非常流行的 JSON 序列化和反序列化库,ObjectMapper 是它的主要接口,用于处理复杂的对象和数据结构。

1.3.2  objectMapper.registerModule(new JavaTimeModule());

作用:为 ObjectMapper 注册一个 JavaTimeModule 模块。

默认情况下,Jackson 不支持直接序列化和反序列化 Java 8 的日期时间类,如 LocalDateTime、LocalDate 等。如果直接使用 ObjectMapper 处理这些类型,会抛出异常,因为 Jackson 不知道如何正确处理这些类。

JavaTimeModule 是一个扩展模块,专门用于处理 Java 8 引入的日期和时间类型,比如 LocalDateTime、ZonedDateTime、LocalDate 等。通过注册这个模块,Jackson 就能够正确地序列化这些时间类型为 JSON,并在反序列化时也能将 JSON 转回到这些时间类型对象。

// 注册 JavaTimeModule 之后,Jackson 就可以处理 LocalDateTime 了

objectMapper.registerModule(new JavaTimeModule());

1.3.3  GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

作用:使用上面配置好的 ObjectMapper 创建一个 GenericJackson2JsonRedisSerializer 实例,作为 Redis 的序列化工具即可。

2 问题2 从Redis取出数据转换为Java对象

2.1 引入:

       在从Redis中读取通过以上方式存储的序列化后的数据,假如你存入的是一个User 对象,User 有一个LocalDateTime类型的属性,你通过上面ObjectMapper序列化器存储到Redis中,若直接使用User user = (User) redisTemplate.opsForValue().get(key);则会出现如下错误:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.seckill.pojo.User

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.seckill.pojo.User
	at com.seckill.service.impl.UserServiceImpl.getUserByCookie(UserServiceImpl.java:98)
	at com.seckill.service.impl.UserServiceImpl$$FastClassBySpringCGLIB$$d92357c4.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
	at com.seckill.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$16ffc976.getUserByCookie(<generated>)
	at 

2.2 解决方案

因为包含LocalDateTime等Java8中的新类,从Redis取数据时默认会返回一个LinkedHashMap,其中keys为属性名,values为属性值。因此需要使用ObjectMapper进行类型转换。

 YourClass test = objectMapper.convertValue(redisTemplate.opsForValue().get(key),YourClass.class);

即可将获得的数据由LinkedHashMap转为对应的类别了。

3 序列化优点

1)Redis 是数据存储系统,而非 Java 对象存储系统

• Redis 支持的基础数据类型有字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等,无法直接理解和处理复杂的 Java 对象。

因此,必须将 Java 对象转化为 Redis 能够理解的格式,例如字符串或二进制字节流,这就需要序列化操作。

2)跨平台和跨语言的兼容性

• Redis 是一个与语言无关的存储系统,支持多种语言的客户端(如 Java、Python、Go 等)。序列化将 Java 对象转换为通用格式,如 JSON、XML 或二进制流,使其可以在不同平台或语言之间互操作。

• 例如,JSON 序列化可以让其他语言的客户端读取 Java 系统存入 Redis 的数据。

3)Java 对象结构复杂

• Java 对象不仅仅是基本数据类型,它可能包含嵌套的对象、集合、数组等复杂结构。这些复杂结构不能简单地转换为 Redis 可识别的字符串表示。

• 通过序列化,可以将整个对象结构转化为线性的、可传输的字节流,从而可以存储到 Redis。

4)性能优化

• 序列化后的数据通常是二进制或字符串表示,这些格式的数据可以被 Redis 高效地存储和操作,提升 Redis 的读写性能。

• 例如,使用二进制序列化可以节省存储空间,同时减少网络传输时的带宽消耗。

4 objectMapper.convertValue()

4.1 原理

ObjectMapper.convertValue() 是 Jackson 库中的方法,用于将一个对象(通常是 Map 或者其他类型的对象)转换为另一种类型。其工作原理基于 Jackson 库强大的序列化和反序列化机制,背后的基本流程是:

1. 数据的中间表示:在内部,ObjectMapper 会先将输入对象(例如 LinkedHashMap 或其他对象)转换成一种中间表示(类似 JSON 数据的内部结构),这个中间表示可以是一个 Map、数组、字符串等。

2. 类型推断和映射:convertValue() 方法会根据你传入的目标类型(例如 YourClass.class),将中间表示映射到该目标类型的字段中。这个过程类似于反序列化的过程,即 Jackson 会查看目标类中的字段和类型信息,并试图将中间表示中的数据逐一填充到目标对象的对应字段。

3. 属性映射和类型转换:convertValue() 通过 Jackson 的反射机制读取目标类的属性,并将输入对象中的数据映射到这些属性上。在这个过程中,Jackson 能自动处理各种类型转换,包括基本类型、复杂对象、集合类型,甚至像 LocalDateTime 这样的自定义对象类型(前提是 Jackson 已经配置了对这些类型的支持,例如通过 JavaTimeModule 模块)。

4.2 基本步骤

当调用 convertValue(sourceObject, TargetClass.class) 时,基本流程如下:

Step 1: 将 sourceObject 转换成内部的 JSON 树结构(例如 LinkedHashMap 等)。

Step 2: 根据目标类 TargetClass 的结构,Jackson 查找和匹配相应的属性。

Step 3: 将中间 JSON 树结构中的数据逐步填充到目标类的实例中。

Step 4: 返回填充好的目标类对象。

4.3 示例

//假设 Redis 中存储的数据反序列化为 LinkedHashMap:

Map<String, Object> redisData = redisTemplate.opsForValue().get("someKey");

//这个 redisData 包含了 TestPojo 对象的所有字段和对应的值。然后通过 convertValue,可以将其转换为 TestPojo 对象:

YourClass yourClass = objectMapper.convertValue(redisData, YourClass.class);

//Jackson 将通过字段名称和类型推断,将 redisData 中的值映射到 YourClass 的字段中。


http://www.kler.cn/a/376074.html

相关文章:

  • HtmlAgilityPack 操作详解
  • STM32中ARR(自动重装寄存器)为什么要减1
  • NuGet Next发布,全新版私有化NuGet管理
  • Manus在虚拟现实仿真模拟中的应用案例分享
  • LINUX Shell命令中$0、$1-9、$#、$?、$*、$@、$!、$、$-、$IFS含义及举例
  • Gitee push 文件
  • R数据结构向量基础
  • 公有云开发基础教程
  • 汽车固态电池深度报告
  • 4K双模显示器值得买吗?
  • Python WordCloud库与jieba分词生成词云图的完整指南
  • Ollama:本地部署与运行大型语言模型的高效工具
  • 在kanzi 3.9.8里使用API创建自定义材质
  • PHP反序列化原生类字符串逃逸框架反序列化利用
  • 奥数与C++小学四年级(第十七题 弹跳板)
  • blender雕刻基础 笔记
  • Python毕业设计选题:基于django+vue的宠物寄养平台的设计与实现
  • element根据输入,动态生成表格
  • 【MySQL】MySQL安装以及各种报错处理
  • 全国高校计算机能力挑战赛 Python
  • 【Linux系统编程】第三十九弹---探索信号处理的奥秘:阻塞信号与sigset_t的深入剖析及实战
  • springboot河南旅游推荐系统-计算机毕业设计源码33358
  • 将机器人六轴坐标转为4*4矩阵(Opencv/C++)
  • PHP决策多功能投票小程序系统源码
  • QT for android 问题总结(QT 5.15.2)
  • 【自动化测试】APP UI 自动化(安卓)-本地环境搭建