1、pom文件中引入依赖
redis的依赖配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置文件中的配置
spring:
redis:
# Redis的服务器地址
host: localhost
# Redis的连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 1000
# Redis数据库索引(默认为0);Redis数据库默认有16个独立的数据库,编号0-15
database: 0
3、为什么要自定义redisTemplate
一般情况下,配置完以上的内容就可以直接使用RedisTemplate了,但是当数据存储到Redis的时候,键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。
RedisTemplate默认使用的是JdkSerializationRedisSerializer;
StringRedisTemplate默认使用的是StringRedisSerializer。
Spring Data JPA为我们提供了下面的Serializer:
- GenericToStringSerializer
- Jackson2JsonRedisSerializer
- JacksonJsonRedisSerializer
- JdkSerializationRedisSerializer
- OxmSerializer
- StringRedisSerializer。
序列化方式对比:
-
JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是实体类需要实现Serializable接口,如果操作的实体类没有实现Serializable接口就会报IllegalArgumentException错误;还有序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存;
-
Jackson2JsonRedisSerializer: 使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍,不需要实现Serializable接口。但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息。
注意: 在使用redisTemplate时,如果使用Jackson2JsonRedisSerializer、JdkSerializationRedisSerializer对值进行序列化的话,然后对值进行自增计算的时候会报错:ERR value is not an integer or out of range。
原因分析: Jackson2JsonRedisSerializer是先将对象转化为json字符串然后进行存储;JdkSerializationRedisSerializer对象序列化后的值有类信息、版本号等,所以是一个包含很多字母的字符串,所以根本无法加1;
解决办法: 如果需要自增计算的话可以使用StringRedisSerializer序列化器、GenericToStringSerializer序列化器,因为他们将字符串的值直接转为字节数组,所以保存到redis中是数字,所以可以进行加1 ;
特殊情况: 如果redis中先存储了StringRedisSerializer或GenericToStringSerializer序列化之后的值,然后又在使用Jackson2JsonRedisSerializer或者JdkSerializationRedisSerializer进行自增之类的计算时仍然可用,不会报错。
本小节内容参考博客:Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer
另外推荐一篇不错的博文:SpringBoot整合Redis—自定义配置
4、Redis自定义配置类
@Configuration
@SuppressWarnings({ "rawtypes", "unchecked" })
public class RedisConfig {
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(factory);
// 设置value的序列化方式
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
// 可视化设置
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
// mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //已经过时,用下边的设置代替
mapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
jacksonSeial.setObjectMapper(mapper);
// 设置key的序列化方式,一般选用StringRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 设置value的序列化方式
redisTemplate.setValueSerializer(jacksonSeial);
// 设置hash key 和hash value序列化模式
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jacksonSeial);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
5、使用测试
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@RequestMapping(value = "/user", method = RequestMethod.GET)
public void getUserInfo() {
User user = new User();
user.setName("zhangsan");
user.setSex("男");
user.setAge(12);
System.out.println("user:" + user);
redisTemplate.opsForValue().set(user.getName(), user);
User user1 = (User) redisTemplate.opsForValue().get(user.getName());
System.out.println("user1:" + user1);
}
user实体类就三个字段,姓名、性别、年龄;这里就不给出了。
6、常见问题
问题1: 当使用 “Jackson2JsonRedisSerializer” 对值进行序列化,当获取数据时报如下所示的错误:
java.util.LinkedHashMap cannot be cast to com.learn.entity.Student
出现这个问题的原因是因为没有设置"DefaultTyping",当不设置这个参数时,序列化之后的数据是一个纯Json,不包含类型,设置这个参数的方式如下:
ObjectMapper mapper = new ObjectMapper();
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛异常
// mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //已经过时,用下边的设置代替
mapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
来看下设置以后Redis中存储的数据格式有什么不同:
设置之后
[
"java.util.ArrayList", // 包含类型
[
{
"@class": "com.learn.entity.Student", // 包含类型
"id": "5f51b23d34f9c5155cda1ab4",
"name": "张三",
"sex": "男",
"school": {
"@class": "com.learn.entity.School",
"id": "5f51b23d34f9c5155cda1ab3",
"students": null,
"name": "BJ_university"
}
}
]
]
设置之前
[
{
"id": "5f51b23d34f9c5155cda1ab4",
"name": "张三",
"sex": "男",
"school": {
"id": "5f51b23d34f9c5155cda1ab3",
"students": null,
"name": "BJ_university"
}
}
]
这样当我们获取数据时就可以直接转化为List格式的数据,方便直接使用。