应用场景

我们希望通过缓存来减少对关系型数据库的查询次数,减轻数据库压力。在执行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缓存数据库查询结果中序列化问题的解决的更多相关文章

  1. Spring Security教程(二):自定义数据库查询

    Spring Security教程(二):自定义数据库查询   Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就 ...

  2. spring Cache /Redis 缓存 + Spring 的集成示例

    spring Cache https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ spring+redis 缓存 ht ...

  3. 【快学SpringBoot】Spring Cache+Redis实现高可用缓存解决方案

    前言 之前已经写过一篇文章介绍SpringBoot整合Spring Cache,SpringBoot默认使用的是ConcurrentMapCacheManager,在实际项目中,我们需要一个高可用的. ...

  4. spring cache redis

    一.使用开源包(spring-data-redis) 1.引入jar包 <dependency>      <groupId>org.springframework.data& ...

  5. spring data redis jackson 配置,工具类

    spring data redis 序列化有jdk .jackson.string 等几种类型,自带的jackson不熟悉怎么使用,于是用string类型序列化,把对象先用工具类转成string,代码 ...

  6. Spring+ehcache+redis两级缓存

    问题描述 场景:我们的应用系统是分布式集群的,可横向扩展的.应用中某个接口操作满足以下一个或多个条件: 1. 接口运行复杂代价大, 2. 接口返回数据量大, 3. 接口的数据基本不会更改, 4. 接口 ...

  7. Spring Cache Redis结合遇到的坑

    业务上需要把一些数据放到redis里面,但是系统逻辑代码差不多编写完成了,怎么整?用Spring Cache啊,对既有业务逻辑侵袭极小. 于是尝试调查了一下,遇到一些问题分享一下(本文使用Spring ...

  8. spring Cache + Redis 开发数据字典以及自定义标签

    一.数据库表结构 1.  分类表:dict_type 2.  子项表:dict_entry 二.页面维护功能示意图: 1.  分类管理 点击子项管理进入子项管理页面 2.子项管理 三.数据字典添加到缓 ...

  9. Redis非关系型缓存数据库集群部署、参数、命令工具

    <关系型数据库与非关系型数据库> 关系数据库:mysql.oracle.DB2.SQL Server非关系数据库:Redis(缓存数据库).MongodDB(处理海量数据).Memcach ...

随机推荐

  1. 使用pageHelper分页查询,报sql语句错误

    1.异常详情:  2.异常分析: (1)pageHelper分页大致流程: 配置默认的拦截器:pagehelper.PageInterceptor,对发送的查询语句进行拦截,拦截之后对原有的查询语句进 ...

  2. Java笔记 - 线程与并行API

    一.线程简介 1.线程与进程 每个进程都具有独立的代码和数据空间,进程间的切换会有较大的开销.线程是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开 ...

  3. python3快速安装

    linux环境快速安装python3   之前在linux上安装python3的时候,为了让不影响linux环境原有的python2的环境,选择的方法都是下载对应的linux环境的python包,不过 ...

  4. Android基础控件Button的使用

    1.相关属性 Android的按钮有Button和ImageButton(图像按钮),Button extends TextView, ImageButton extends ImageView! a ...

  5. SPSS超详细操作:分层回归(hierarchical multiple regression)

    SPSS超详细操作:分层回归(hierarchical multiple regression) 1.问题与数据 最大携氧能力(maximal aerobic capacity, VO2max)是评价 ...

  6. Jetty启动配置解析

    目录 1. jetty概述 2. spring-jetty启动配置 1. jetty概述 维基百科:Jetty是一个纯粹的基于Java的网页服务器和Java Servlet容器. Jetty Serv ...

  7. svn both sides of the move must be committed together

    从一个模块移动文件到另一个模块下,提交的时候报错. 选择2个模块工程,team-与资源库同步 选择2个模块下,2份文件,一份减号图标,一份加号图标,同时提交. 还有种情况,当你重名文件后,提交报错 打 ...

  8. java浮点运算的陷阱

    首先呢,i你要明白double和float. Inifinity: 例如:syso(5.0/0.0+" 和"+0.0 /0.0); 浮点运算的0.0不是真正意义上的0,而是非常接近 ...

  9. [Turn]C# 强制关闭当前程序进程(完全Kill掉不留痕迹)

    C#代码 /// <summary> /// 运行DOS命令 /// DOS关闭进程命令(ntsd -c q -p PID )PID为进程的ID /// </summary> ...

  10. vue @scroll

    参考: https://blog.csdn.net/qq449736038/article/details/80769507