使用Spring Data Redis时,遇到的几个问题

简介: 使用Spring Data Redis时,遇到的几个问题http://www.bieryun.com/3424.html 需求: 1,保存一个key-value形式的结构到Redis 2,把一个对象保存成hash形式的结构到redis 代码如下: // 保存key-value值 pushFrequencyTemplate.

使用Spring Data Redis时,遇到的几个问题http://www.bieryun.com/3424.html

需求:

1,保存一个key-value形式的结构到Redis

2,把一个对象保存成hash形式的结构到redis

代码如下:

// 保存key-value值
pushFrequencyTemplate.opsForValue().set("test_key", "test_value111");
// 读取刚才保存的key-value值
System.out.println(pushFrequencyTemplate.opsForValue().get("test_key"));

// 把对象保存成hash
Map<String, String> map = frequencyMapper.toHash(frequency);
pushFrequencyTemplate.opsForHash().putAll("push_frequency", map);
// 把刚才保存的hash读出来,并显示
Map<String, String> redisMap = pushFrequencyTemplate.opsForHash().entries("push_frequency");
RedisPushFrequencyEntity redisFrequency = frequencyMapper.fromHash(redisMap);
System.out.println(redisMap);

问题1:

声明一个redisTemplate,测试是否可以把对象保存成hash,并从hash还原成对象。

只设置ConnectionFactory,其它什么也不设置。代码如下:

@Bean(name = "pushFrequencyTemplate")
public <String, V> RedisTemplate<String, V> getPushFrequencyTemplate() {
RedisTemplate<String, V> redisTemplate = new RedisTemplate<String, V>();
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}

结果:

get test_key// 返回为空(什么也不显示)

hgetall push_frequency // 返回为空(什么也不显示)

很奇怪为什么为空,因为查了一些资料,如果不进行设置的话,默认使用JdkSerializationRedisSerializer进行数据序列化。

(把任何数据保存到redis中时,都需要进行序列化)

用视图的形式查了一下,发现实际保存的内容如下:

key-value:

key:\xAC\xED\x00\x05t\x00\x08test_key

value:\xAC\xED\x00\x05t\x00\x0Dtest_value111

hash:

key:\xAC\xED\x00\x05t\x00\x0Epush_frequency

hashkey:\xAC\xED\x00\x05t\x00\x04date

hashvalue:\xAC\xED\x00\x05t\x00\x0A2016-08-18

hashkey:\xAC\xED\x00\x05t\x00\x09frequency

hashvalue:\xAC\xED\x00\x05t\x00\x011

所有的key和value还有hashkey和hashvalue的原始字符前,都加了一串字符。查了一下,这是JdkSerializationRedisSerializer进行序列化时,加上去的。

原以为只会在value或hashvalue上加,没想到在key和hashkey上也加了,这样的话,用原来的key就取不到我们保存的数据了。

所以,我们要针对我们的需求,设置RedisSerializer。

现在可用的RedisSerializer主要有几种:

(1)StringRedisSerializer

(2)Jackson2JsonRedisSerializer

(3)JdkSerializationRedisSerializer

(4)GenericToStringSerializer

(5)OxmSerializer

StringRedisSerializer:对String数据进行序列化。序列化后,保存到Redis中的数据,不会有像上面的“\xAC\xED\x00\x05t\x00\x09”多余字符。就是"frequency".

Jackson2JsonRedisSerializer:用Jackson2,将对象序列化成Json。这个Serializer功能很强大,但在现实中,是否需要这样使用,要多考虑。一旦这样使用后,要修改对象的一个属性值时,就需要把整个对象都读取出来,再保存回去。

JdkSerializationRedisSerializer:使用Java序列化。结果就像最上面的样子。

GenericToStringSerializer:使用spring转换服务进行序列化。在网上没有找到什么例子,使用方法和StringRedisSerializer相比,StringRedisSerializer只能直接对String类型的数据进行操作,如果要被序列化的数据不是String类型的,需要转换成String类型,例如:String.valueOf()。但使用GenericToStringSerializer的话,不需要进行转换,直接由String帮我们进行转换。但这样的话,也就定死了序列化前和序列化后的数据类型,例如:template.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));

我们只能用对Long型进行序列化和反序列化。(但基础类型也不多,定义8个可能也没什么)

OxmSerializer:使用SpringO/X映射的编排器和解排器实现序列化,用于XML序列化。

我们这里针对StringRedisSerializer,Jackson2JsonRedisSerializer和JdkSerializationRedisSerializer进行测试。

下面是,把3种Serializer保存到Redis中的结果:

1,所有的KeySerializer和HashKeySerializer都使用StringRedisSerializer,用其它Serializer的没有什么意义,就像最上面的例子一样。
2,上面序列化后的值,是保存到redis中的值,从Redis中读取回Java中后,值的内容都是一样的。

从上面的结果不难看出,

1,用StringRedisSerializer进行序列化的值,在Java和Redis中保存的内容是一样的

2,用Jackson2JsonRedisSerializer进行序列化的值,在Redis中保存的内容,比Java中多了一对双引号。

3,用JdkSerializationRedisSerializer进行序列化的值,对于Key-Value的Value来说,是在Redis中是不可读的。对于Hash的Value来说,比Java的内容多了一些字符。

(如果Key的Serializer也用和Value相同的Serializer的话,在Redis中保存的内容和上面Value的差异是一样的,所以我们保存时,只用StringRedisSerializer进行序列化)

问题2:

当想把一个对象保存成一个Hash的时候,用Spring提供的HashMapper相关类,进行转换。看了一些例子,使用方法如下:

private final HashMapper<User, String, String> mapper =
new DecoratingStringHashMapper<User>(new BeanUtilsHashMapper<User>(User.class));

// 把对象保存成hash
Map<String, String> map = mapper.toHash(user);
pushFrequencyTemplate.opsForHash().putAll("user", map);
// 把刚才保存的hash读出来,并显示
Map<String, String> redisMap = pushFrequencyTemplate.opsForHash().entries("user");

DecoratingStringHashMapper和BeanUtilsHashMapper都实现了HashMapper接口,例子中是嵌套使用的,能不能不嵌套使用,只使用BeanUtilsHashMapper呢。

试了一下,出错了。

private final HashMapper<DwUser, String, String> mapper = new BeanUtilsHashMapper<User>(User.class);

看了一下代码,没具体测试和细看,好像Spring的PutAll方法,接收的是一种LinkedHashMap的Map,其它的会报错。

问题3:

把对象转换成Map的实现类,原来有2个:BeanUtilsHashMapper和JacksonHashMapper。但在1.7版本时,JacksonHashMapper不被推荐使用了,所以使用了BeanUtilsHashMapper。但BeanUtilsHashMapper有一个问题,它会把对象中所有的getter方法都把取出来,把get后面的字符串当成属性放到map里。所以每个对象都有的getClass方法也被当成一个属性,放到map里了,不得不手工把这个属性删除一下。

为了避免这样的重复手工劳动,写了一个类来实现这个工作:

共通类:

import java.util.Map;

import org.springframework.data.redis.hash.BeanUtilsHashMapper;
import org.springframework.data.redis.hash.DecoratingStringHashMapper;
import org.springframework.data.redis.hash.HashMapper;

public class HashMapper<T, K, V> implements HashMapper<T, K, V> {

private HashMapper<T, K, V> mapper;

public HashMapper(HashMapper<T, K, V> mapper) {
// this.mapper = mapper;
this.mapper = mapper;
}

@Override
public Map<K, V> toHash(T obj) {
Map<K, V> map = mapper.toHash(obj);
// 去掉Object类中的class属性生成的key/value
map.remove("class");
return map;
}

@Override
public T fromHash(Map<K, V> map) {
return mapper.fromHash(map);
}

public static <T, K, V> HashMapper<T, K, V> getInstance(Class<T> tClazz, Class<K> kClazz,
Class<V> vClazz) {
return new HashMapper<T, K, V>((HashMapper<T, K, V>) new DecoratingStringHashMapper<T>(
new BeanUtilsHashMapper<T>(tClazz)));
}

}

使用方法:

// 声明

private final HashMapper<RedisPushFrequencyEntity, String, String> frequencyMapper =
MOKOHashMapper.getInstance(RedisPushFrequencyEntity.class, String.class, String.class);

// 使用

frequencyMapper.toHash(xxx);

问题4:

如果想使用RedisTemplate来帮助你,把从Redis中取得的值直接转换成对象等数据类型的话,

必须得像下面一样声明,有多少个转换的话,就要声明多少个RedisTemplate。

声明RedisTemplate:

@Bean(name = "userRedisTemplate")
public RedisTemplate<String, User> getRedisTemplate() {
RedisTemplate<String, User> redisTemplate = new RedisTemplate<String, User>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<User>(User.class));
redisTemplate.setDefaultSerializer(new StringRedisSerializer());
return redisTemplate;
}

使用地方:

@Autowired
private RedisTemplate<String, DwUser> userRedisTemplate;

试了一下,可以写一个共通方法来把上面的做法简化一下。

共通方法:

public <String, V> RedisTemplate<String, V> getJacksonStringTemplate(Class<V> clazz) {
RedisTemplate<String, V> redisTemplate = new RedisTemplate<String, V>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<V>(clazz));
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
// 不是注入方法的话,必须调用它。Spring注入的话,会在注入时调用
redisTemplate.afterPropertiesSet();

return redisTemplate;
}

使用地方:

private RedisTemplate<String, RedisPushFrequencyEntity> keyJacksonValueTemplate;

@PostConstruct
public void PushRedisServicePostConstruct() {
keyJacksonValueTemplate =
redisTemplateFactory.getJacksonStringTemplate(RedisPushFrequencyEntity.class);
}

*  1,RedisTemplate声明时,不能使用@Autowire自动注入
*  2,调用下面的方法时行初始化时,必须在@PostConstruct方法中去做。

问题5:

Spring Data里还有一些Redis类,在包下面,

例如:RedisAtomicInteger, RedisAtomicLong, RedisList, RedisSet, RedisZSet, RedisMap

粗略看了一下,这些类的实现,都是使用上面的RedisTemplate的各种方法来实现的,方便使用。

下面的文章和retwisj项目都介绍了一些上面的类的使用方法,可以看看。

http://www.cnblogs.com/qijiang/p/5626461.html

问题6:

如果我想用Jedis原生接口怎么,也有办法:

(ValueOperation,ListOperation,SetOperation等操作也都是用它实现的,可以看看源码)

redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
connection.set(
redisTemplate.getStringSerializer().serialize(
"user.uid." + user.getUid()),
redisTemplate.getStringSerializer().serialize(
user.getAddress()));
return null;
}
});

相关文章
|
8月前
|
NoSQL Java 数据库连接
《深入理解Spring》Spring Data——数据访问的统一抽象与极致简化
Spring Data通过Repository抽象和方法名派生查询,简化数据访问层开发,告别冗余CRUD代码。支持JPA、MongoDB、Redis等多种存储,统一编程模型,提升开发效率与架构灵活性,是Java开发者必备利器。(238字)
|
8月前
|
存储 Java 关系型数据库
Spring Boot中Spring Data JPA的常用注解
Spring Data JPA通过注解简化数据库操作,实现实体与表的映射。常用注解包括:`@Entity`、`@Table`定义表结构;`@Id`、`@GeneratedValue`配置主键策略;`@Column`、`@Transient`控制字段映射;`@OneToOne`、`@OneToMany`等处理关联关系;`@Enumerated`、`@NamedQuery`支持枚举与命名查询。合理使用可提升开发效率与代码可维护性。(238字)
718 1
存储 JSON Java
942 0
|
9月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
814 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
9月前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
853 0
|
11月前
|
NoSQL Java Redis
Redis基本数据类型及Spring Data Redis应用
Redis 是开源高性能键值对数据库,支持 String、Hash、List、Set、Sorted Set 等数据结构,适用于缓存、消息队列、排行榜等场景。具备高性能、原子操作及丰富功能,是分布式系统核心组件。
805 2
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
384 32
|
存储 缓存 NoSQL
Redis 缓存 + Spring 的集成示例(转)
《整合 spring 4(包括mvc、context、orm) + mybatis 3 示例》一文简要介绍了最新版本的 Spring MVC、IOC、MyBatis ORM 三者的整合以及声明式事务处理。
1607 0
|
存储 缓存 NoSQL
Redis 缓存 + Spring 的集成示例
SpringSession和Redis实现Session跨域 http://www.ithao123.cn/content-11111681.html   tomcat中创建session很耗服务器内存 原生session与session in redis对比下面是从stackoverflo...
1627 0