使用Spring Cache + Redis + Jackson Serializer缓存数据库查询结果中序列化问题的解决
应用场景
我们希望通过缓存来减少对关系型数据库的查询次数,减轻数据库压力。在执行DAO类的select***(), query***()方法时,先从Redis中查询有没有缓存数据,如果有则直接从Redis拿到结果,如果没有再向数据库发起查询请求取数据。
序列化问题
要把domain object做为key-value对保存在redis中,就必须要解决对象的序列化问题。Spring Data Redis给我们提供了一些现成的方案:
JdkSerializationRedisSerializer. 使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。Jackson2JsonRedisSerializer. 使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍。但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息。
如果用方案一,就必须付出缓存多占用4倍内存的代价,实在承受不起。如果用方案二,则必须给每一种domain对象都配置一个Serializer,即如果我的应用里有100种domain对象,那就必须在spring配置文件中配置100个Jackson2JsonRedisSerializer,这显然是不现实的。
通过google, 发现spring data redis项目中有一个#145 pull request, 而这个提交请求的内容正是解决Jackson必须提供类型信息的问题。然而不幸的是这个请求还没有被merge。但我们可以把代码copy一下放到自己的项目中:
/**
* @author Christoph Strobl
* @since 1.6
*/
public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Object> {
private final ObjectMapper mapper;
/**
* Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing.
*/
public GenericJackson2JsonRedisSerializer() {
this((String) null);
}
/**
* Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing using the
* given {@literal name}. In case of an {@literal empty} or {@literal null} String the default
* {@link JsonTypeInfo.Id#CLASS} will be used.
*
* @param classPropertyTypeName Name of the JSON property holding type information. Can be {@literal null}.
*/
public GenericJackson2JsonRedisSerializer(String classPropertyTypeName) {
this(new ObjectMapper());
if (StringUtils.hasText(classPropertyTypeName)) {
mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName);
} else {
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
}
}
/**
* Setting a custom-configured {@link ObjectMapper} is one way to take further control of the JSON serialization
* process. For example, an extended {@link SerializerFactory} can be configured that provides custom serializers for
* specific types.
*
* @param mapper must not be {@literal null}.
*/
public GenericJackson2JsonRedisSerializer(ObjectMapper mapper) {
Assert.notNull(mapper, "ObjectMapper must not be null!");
this.mapper = mapper;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.serializer.RedisSerializer#serialize(java.lang.Object)
*/
@Override
public byte[] serialize(Object source) throws SerializationException {
if (source == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return mapper.writeValueAsBytes(source);
} catch (JsonProcessingException e) {
throw new SerializationException("Could not write JSON: " + e.getMessage(), e);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.serializer.RedisSerializer#deserialize(byte[])
*/
@Override
public Object deserialize(byte[] source) throws SerializationException {
return deserialize(source, Object.class);
}
/**
* @param source can be {@literal null}.
* @param type must not be {@literal null}.
* @return {@literal null} for empty source.
* @throws SerializationException
*/
public <T> T deserialize(byte[] source, Class<T> type) throws SerializationException {
Assert.notNull(type,
"Deserialization type must not be null! Pleaes provide Object.class to make use of Jackson2 default typing.");
if (SerializationUtils.isEmpty(source)) {
return null;
}
try {
return mapper.readValue(source, type);
} catch (Exception ex) {
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
}
}
}
然后在配置文件中使用这个GenericJackson2JsonRedisSerializer:
<bean id="jacksonSerializer" class="com.fh.taolijie.component.GenericJackson2JsonRedisSerializer">
</bean>
重新构建部署,我们发现这个serializer可以同时支持多种不同类型的domain对象,问题解决。
使用Spring Cache + Redis + Jackson Serializer缓存数据库查询结果中序列化问题的解决的更多相关文章
- Spring Security教程(二):自定义数据库查询
Spring Security教程(二):自定义数据库查询 Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就 ...
- spring Cache /Redis 缓存 + Spring 的集成示例
spring Cache https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ spring+redis 缓存 ht ...
- 【快学SpringBoot】Spring Cache+Redis实现高可用缓存解决方案
前言 之前已经写过一篇文章介绍SpringBoot整合Spring Cache,SpringBoot默认使用的是ConcurrentMapCacheManager,在实际项目中,我们需要一个高可用的. ...
- spring cache redis
一.使用开源包(spring-data-redis) 1.引入jar包 <dependency> <groupId>org.springframework.data& ...
- spring data redis jackson 配置,工具类
spring data redis 序列化有jdk .jackson.string 等几种类型,自带的jackson不熟悉怎么使用,于是用string类型序列化,把对象先用工具类转成string,代码 ...
- Spring+ehcache+redis两级缓存
问题描述 场景:我们的应用系统是分布式集群的,可横向扩展的.应用中某个接口操作满足以下一个或多个条件: 1. 接口运行复杂代价大, 2. 接口返回数据量大, 3. 接口的数据基本不会更改, 4. 接口 ...
- Spring Cache Redis结合遇到的坑
业务上需要把一些数据放到redis里面,但是系统逻辑代码差不多编写完成了,怎么整?用Spring Cache啊,对既有业务逻辑侵袭极小. 于是尝试调查了一下,遇到一些问题分享一下(本文使用Spring ...
- spring Cache + Redis 开发数据字典以及自定义标签
一.数据库表结构 1. 分类表:dict_type 2. 子项表:dict_entry 二.页面维护功能示意图: 1. 分类管理 点击子项管理进入子项管理页面 2.子项管理 三.数据字典添加到缓 ...
- Redis非关系型缓存数据库集群部署、参数、命令工具
<关系型数据库与非关系型数据库> 关系数据库:mysql.oracle.DB2.SQL Server非关系数据库:Redis(缓存数据库).MongodDB(处理海量数据).Memcach ...
随机推荐
- js 数据绑定
// 回流:(重排 reflow) 当HTML的DOM结构(删除.增加.位置等)发生改变时引起DOM回流.浏览器重新计算DOM结构,重新的对当前DOM结构进行渲染 // 重绘:某一个元素的部分 ...
- 怎样理解js数组中indexOf()的用法与lastIndexOf
第一首先你运行一下它的js代码: var arr1=["大学","中庸","论语","孟子","诗" ...
- 转-Pycharm中运行Python代码的几种方式
转自:Pycharm中运行Python代码的几种方式 在pycharm中的Python代码运行会出现各种奇葩的问题,比如,密码输入时不显示或没有提示,给我们带来一些麻烦,下面介绍几种代码运行的几种方式 ...
- javaweb的几种开发模式
1.MVC模式基础 1.1.MVC模式简介 MVC是一种架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而达到更好的开发和维护效率.在MVC模式中 ...
- tiger-complier 问题记录 类型检查
1 注意区分 Ty_xxx 和 a_ 的区别 A 开头是对应的是原程序中的一个实体 Ty _ 开头则是类型检查中的一个抽象类 代表了一类实体 比如 A_recordTy 是原程序中的 type per ...
- 2019-8-31-C#-已知点和向量,求距离的点
title author date CreateTime categories C# 已知点和向量,求距离的点 lindexi 2019-08-31 16:55:58 +0800 2018-05-08 ...
- 2019-8-31-C#-性能分析-反射-VS-配置文件-VS-预编译
title author date CreateTime categories C# 性能分析 反射 VS 配置文件 VS 预编译 lindexi 2019-08-31 16:55:58 +0800 ...
- Cat- Linux必学的60个命令
1.作用 cat(“concatenate”的缩写)命令用于连接并显示指定的一个和多个文件的有关信息,它的使用权限是所有用户. 2.格式 cat [options] 文件1 文件2…… 3.[opti ...
- 阿里云 Aliplayer高级功能介绍(五):多语言
基本介绍 Aliplayer默认支持中文和英文,并且依赖于浏览器的语言设置自动启用中文或英文资源, 除了支持这两种资源外,还提供自定义语言的形式,支持其他国际语言,另外Aliplayer还支持点播服务 ...
- pip报错ImportError: cannot import name main
编辑pip sudo gedit /usr/bin/pip 修改pip文件: 源文件 from pip import main if __name__ == '__main__': sys.exit( ...