Spring Data Redis对象缓存序列化问题

相信在项目中,你一定是经常使用 Redis ,那么,你是怎么使用的呢?在使用时,有没有遇到同我一样,对象缓存序列化问题的呢?那么,你又是如何解决的呢?

Redis 使用示例

添加依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在应用启动如何添加启用缓存注解(@EnableCaching)。

假如我们有一个用户对象(UserVo):

@Data
public class UserVo implements Serializable {@Serialprivate static final long serialVersionUID = 2215423070276994378L;private Long id;private String name;private LocalDateTime createDateTime;}

这里,我们实现了 Serializable 接口。

在我们需要缓存的方法上,使用 @Cacheable 注解,就表示如果返回的对象不是 null 时,就会对其进行缓存,下次查询,首先会去缓存中查询,查到了,就直接返回,不会再去数据库查询,查不到,再去数据库查询。

@Service
@Slf4j
public class UserServiceImpl implements IUserService {@Override@Cacheable(value = "sample-redis",key = "'user-'+#id",unless = "#result == null")public UserVo getUserById(Long id) {log.info("userVo from db query");UserVo userVo = new UserVo();userVo.setId(1L);userVo.setName("Zhang San");userVo.setCreateDateTime(LocalDateTime.now());return userVo;}}

核心代码:

@Cacheable(value = "sample-redis",key = "'user-'+#id",unless = "#result == null"
)

模拟测试,再写一个测试接口:

@RestController
@RequestMapping("/sample")
@RequiredArgsConstructor
@Slf4j
public class SampleController {private final IUserService userService;@GetMapping("/user/{id}")public UserVo getUserById(@PathVariable Long id) {UserVo vo = userService.getUserById(id);log.info("vo: {}", JacksonUtils.json(vo));return vo;}}

我们再加上连接 redis 的配置:

spring:data:redis:host: localhostport: 6379

测试:

### getUserById
GET http://localhost:8080/sample/user/1

在这里插入图片描述

输出结果跟我们想的一样,第一次从数据库查,后面都从缓存直接返回。

总结一下:

  1. 添加 spring-boot-starter-data-redis 依赖。

  2. 使用启用缓存注解(@EnableCaching)。

  3. 需要缓存的对象实现 Serializable 接口。

  4. 使用 @Cacheable 注解缓存查询的结果。

遇到问题

在上面我们通过 spring boot 提供的 redis 实现了查询对象缓存这样一个功能,有下面几个问题:

  1. 缓存的对象,必须序列化,不然会报错。
  2. redis 存储的数据,看不懂,可以转成 json 格式吗?
  3. 使用 Jackson 时,遇到特殊类型的字段会报错,比如 LocalDateTime。

第1个问题,如果对象没有实现 Serializable 接口,会报错:

在这里插入图片描述

关键信息:

java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [xxx.xxx.UserVo]

我详细描述一下第3个问题,默认是使用 Jdk序列化 JdkSerializationRedisSerializer,redis 里面存的数据如下:

在这里插入图片描述

问题很明显,对象必须要实现序列化接口,存的数据不易查看,所以,改用 GenericJackson2JsonRedisSerializer ,这就有了第3个问题。

我们加上下面的配置,就能解决第2个问题。

@Bean
public RedisCacheConfiguration redisCacheConfiguration() {return RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
}

下面看第三个问题的错误:

在这里插入图片描述

如何解决?

既然有了明确的错误提示,那也是好解决的,我们可以这样:

@JsonDeserialize(using = LocalDateTimeDeserializer.class)		// 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class)		    // 序列化
private LocalDateTime createDateTime;

这样就可以了,我们看下redis里面存的数据:

{"@class":"com.fengwenyi.erwin.component.sample.redis.vo.UserVo","id":1,"name":"Zhang San","createDateTime":[2023,12,29,23,44,3,479011000]}

其实到这里,已经解决了问题,那有没有更省心的办法呢?

解决办法

其实我们知道,使用的就是 Jackson 进行 json 转换,而 json 转换,遇到 LocalDateTime 问题时,我们配置一下 module 就可以了,因为默认用的 SimpleModule,我们改用 JavaTimeModule 就可以了。

这时候问题又来啦,错误如下:

在这里插入图片描述

这时候存的数据如下:

{"id":1,"name":"Zhang San","createDateTime":"2023-12-29T23:31:52.548517"}

这就涉及到 Jackson 序列化漏洞的问题了,采用了白名单机制,我们就粗暴一点:

jsonMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL
);

redis 存的数据如下:

["com.fengwenyi.erwin.component.sample.redis.vo.UserVo",{"id":1,"name":"Zhang San","createDateTime":"2023-12-29T23:56:18.197203"}]

最后,来一段完整的 RedisCacheConfiguration 配置代码:

@Bean
public RedisCacheConfiguration redisCacheConfiguration() {return RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair
//                            .fromSerializer(RedisSerializer.json())
//                            .fromSerializer(
//                                    new GenericJackson2JsonRedisSerializer()
//                            ).fromSerializer(redisSerializer()));
}private RedisSerializer<Object> redisSerializer() {JsonMapper jsonMapper = new JsonMapper();JacksonUtils.configure(jsonMapper);jsonMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);return new GenericJackson2JsonRedisSerializer(jsonMapper);
}

希望今天的分享对你有一定的帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/310150.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IntelliJ IDEA常用快捷键

【1】创建内容&#xff08;新建&#xff09;&#xff1a;altinsert 【2】main方法&#xff1a;psvm 【3】输出语句&#xff1a;sout 【4】复制行&#xff1a;ctrld 【5】删除行&#xff1a;ctrly&#xff08;很多编辑器ctrly是前进操作&#xff0c;如果选择 Delete Line&…

40道MyBatis面试题带答案(很全)

1. 什么是MyBatis &#xff08;1&#xff09;Mybatis是一个半ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了JDBC&#xff0c;开发时只需要关注SQL语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接…

C++编程中级阶段

目录 1.模版 1.1函数模版 1.1.1函数模版语法 1.1.2函数模版注意事项 1.1.3函数模版案例 1.1.4普通函数与函数模板的区别 1.1.5普通函数与函数模板的调用规则 1.1.6模版的局限性 1.2类模版 2.STL处识 3.STL常用容器 3.1string容器 3.2vector容器 3.3deque容器 3.…

工作中常用的 git 命令

了解版本控制 什么是版本控制?我真的需要吗?版本控制是一种记录若干文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。 什么是分布式版本控制系统分布式版本控制系统( Distributed Version Control System&#xff0c;简称 DVCS )。 在这类系统中&#xff0c;像…

2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题)

2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题) 2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题) 第一部分&#xff1a;数据安全防护(30%) 第二部分&#xff1a;数据安全管理(30%) 第三部分&#xff1a;数据安全处置(40%) 项目介绍…

【Transformer】深入理解Transformer模型2——深入认识理解(上)

前言 Transformer模型出自论文&#xff1a;《Attention is All You Need》 2017年 近年来&#xff0c;在自然语言处理领域和图像处理领域&#xff0c;Transformer模型都受到了极为广泛的关注&#xff0c;很多模型中都用到了Transformer或者是Transformer模型的变体&#xff0…

基于轻量级GhostNet模型开发构建生活场景下生活垃圾图像识别系统

轻量级识别模型在我们前面的博文中已经有过很多实践了&#xff0c;感兴趣的话可以自行移步阅读&#xff1a; 《移动端轻量级模型开发谁更胜一筹&#xff0c;efficientnet、mobilenetv2、mobilenetv3、ghostnet、mnasnet、shufflenetv2驾驶危险行为识别模型对比开发测试》 《基…

2023年03月17日_微软和谷歌办公AI的感慨

2023年3月17日 最近这个科技圈的消息 有点爆炸的让人应接不暇了 各种大公司简直就是神仙打架 你从来没有见过这么密集的 这么高频的产品发布 昨天微软是发布了Office 365 Copilot 在里边提供了大量的AI的功能 然后谷歌呢也发布了这个Google Workspace AI 也是跟365 Cop…

macOS系统打开Linux的方法

第一步 按下[command空格键]调出搜索框&#xff0c;输入“终端”&#xff0c;打开图上第一个 第二步 如图先输入"sudo -i"&#xff0c;敲回车键&#xff0c;再输入开机密码&#xff0c;再敲回车键就可以打开。注意&#xff1a;这里的密码输入不会显示在页面。 如果要…

自然语言处理3——玩转文本分类 - Python NLP高级应用

目录 写在开头1. 文本分类的背后原理和应用场景1.1 文本分类的原理1.2 文本分类的应用场景 2. 使用机器学习模型进行文本分类&#xff08;朴素贝叶斯、支持向量机等&#xff09;2.1 朴素贝叶斯2.1.1 基本原理2.1.2 数学公式2.1.3 一般步骤2.1.4 简单python代码实现 2.2 支持向量…

宠物救助上门喂养系统宠物领养宠物寄养寻宠小程序宠物社区系统宠物托运宠物殡葬源码

后端php 前端uniapp mysql数据库 主要功能介绍&#xff1a; 1.根据当前位置 支持多城市切换 2.支持首页公告实时显示 3.支持 宠物救助&#xff0c;上门喂养&#xff0c;宠物领养&#xff0c;宠物寄养&#xff0c;寻宠&#xff0c;宠物社区&#xff0c;宠物托运&#xff…

uni-app uni-app内置组件

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…