SpringBoot整合Redis储存对象

本文介绍如何将Java对象以JSON格式存储到Redis中,并解决在读取过程中出现的类型转换错误问题。通过调整Redis配置使用Jackson序列化器,并对ObjectMapper进行定制,实现正确存储及读取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java对象以json的方式存储到redis中

  1. 通过set进redis中的数据,get不到

     String cityKey ="city_"+id;
     ValueOperations<String,City> operations = this.redisTemplate.opsForValue();
     // 判断缓存中是否存在
     if (this.redisTemplate.hasKey(cityKey)){
    
         City city = operations.get(cityKey);
         return city;
     }
     // 从 DB 中获取
     City city = this.cityRepository.findById(id).get();
    
     // 添加的缓存中,时间10
     operations.set(cityKey,city,100, TimeUnit.SECONDS);
    

在redis-cli 中查看

192.168.6.3:6379> keys *
\xac\xed\x00\x05t\x00\x06city_2
发现key值出现 \xac\xed\x00\x05t\x00\x06city_2 key是乱码

原因:序列化是默认用的JdkSerializationRedisSerializer

在redis配置中添加

//设置key序列-String序列化
template.setKeySerializer(new StringRedisSerializer());
2.如果希望java对象以json的方式存储到redis中

通常会在redis配置中添加

Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

// 设置value的序列化规则
//json序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
但是进行redis缓存get时会报错:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.redis.springbootredis.pojo.City

1 java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.redis.springbootredis.pojo.City
2 at com.redis.springbootredis.service.impl.CityServiceImpl.findCityById(CityServiceImpl.java:59)
3 at com.redis.springbootredis.SpringBootRedisApplicationTests.testFindCityById(SpringBootRedisApplicationTests.java:25)
4 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
5 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
6 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
7 at java.lang.reflect.Method.invoke(Method.java:498)
8 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
9 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
10 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
11 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
12 at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
13 at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
14 at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
15 at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
16 at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
17 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
18 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
19 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
20 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
21 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
22 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
23 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
24 at org.junit.runners.ParentRunner 2. e v a l u a t e ( P a r e n t R u n n e r . j a v a : 268 ) 25 a t o r g . s p r i n g f r a m e w o r k . t e s t . c o n t e x t . j u n i t 4. s t a t e m e n t s . R u n B e f o r e T e s t C l a s s C a l l b a c k s . e v a l u a t e ( R u n B e f o r e T e s t C l a s s C a l l b a c k s . j a v a : 61 ) 26 a t o r g . s p r i n g f r a m e w o r k . t e s t . c o n t e x t . j u n i t 4. s t a t e m e n t s . R u n A f t e r T e s t C l a s s C a l l b a c k s . e v a l u a t e ( R u n A f t e r T e s t C l a s s C a l l b a c k s . j a v a : 70 ) 27 a t o r g . j u n i t . r u n n e r s . P a r e n t R u n n e r . r u n ( P a r e n t R u n n e r . j a v a : 363 ) 28 a t o r g . s p r i n g f r a m e w o r k . t e s t . c o n t e x t . j u n i t 4. S p r i n g J U n i t 4 C l a s s R u n n e r . r u n ( S p r i n g J U n i t 4 C l a s s R u n n e r . j a v a : 190 ) 29 a t o r g . j u n i t . r u n n e r . J U n i t C o r e . r u n ( J U n i t C o r e . j a v a : 137 ) 30 a t c o m . i n t e l l i j . j u n i t 4. J U n i t 4 I d e a T e s t R u n n e r . s t a r t R u n n e r W i t h A r g s ( J U n i t 4 I d e a T e s t R u n n e r . j a v a : 68 ) 31 a t c o m . i n t e l l i j . r t . e x e c u t i o n . j u n i t . I d e a T e s t R u n n e r 2.evaluate(ParentRunner.java:268) 25 at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 26 at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) 27 at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 28 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) 29 at org.junit.runner.JUnitCore.run(JUnitCore.java:137) 30 at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) 31 at com.intellij.rt.execution.junit.IdeaTestRunner 2.evaluate(ParentRunner.java:268)25atorg.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)26atorg.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)27atorg.junit.runners.ParentRunner.run(ParentRunner.java:363)28atorg.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)29atorg.junit.runner.JUnitCore.run(JUnitCore.java:137)30atcom.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)31atcom.intellij.rt.execution.junit.IdeaTestRunnerRepeater.startRunnerWithArgs(IdeaTestRunner.java:47)
32 at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
33 at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
原因:redis中取出的对象数据为json,java.util.LinkedHashMap无法转换成city对象,

解决方式:在redis配置中添加

    //解决查询缓存转换异常的问题
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);

还有一种方式:就是每次获取到对象数据进行json转换(每一都要转换太麻烦不太建议)

        String str = JSONObject.toJSONString(operations.get(cityKey));
        City cityy = JSON.parseObject(str,City.class);

笔者的redis配置类

1 import com.fasterxml.jackson.annotation.JsonAutoDetect;
2 import com.fasterxml.jackson.annotation.PropertyAccessor;
3 import com.fasterxml.jackson.databind.ObjectMapper;
4 import org.springframework.context.annotation.Bean;
5 import org.springframework.context.annotation.Configuration;
6 import org.springframework.data.redis.connection.RedisConnectionFactory;
7 import org.springframework.data.redis.core.RedisTemplate;
8 import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
9 import org.springframework.data.redis.serializer.StringRedisSerializer;
10
11 /**
12 * @Author: GWL
13 * @Description: redis配置
14 * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
15 * 一般redis的序列化方式主要有:字符串序列化、json序列化、xml序列化、jdk序列化
16 * 默认为 JdkSerializationRedisSerializer
17 * @Date: Create in 14:43 2019/5/14
18 */
19 @Configuration
20 public class RedisConfig {
21
22 @Bean
23 public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisCollectionFactory) {
24 RedisTemplate template = new RedisTemplate();
25 template.setConnectionFactory(redisCollectionFactory);
26
27 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
28
29 //解决查询缓存转换异常的问题
30 ObjectMapper om = new ObjectMapper();
31 om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
32 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
33 jackson2JsonRedisSerializer.setObjectMapper(om);
34
35 // 设置value的序列化规则
36 //json序列化
37 template.setValueSerializer(jackson2JsonRedisSerializer);
38 //String序列化
39 // template.setValueSerializer(new StringRedisSerializer());
40 //jdk序列化
41 // template.setValueSerializer(new JdkSerializationRedisSerializer());
42
43 //设置key序列-String序列化
44 template.setKeySerializer(new StringRedisSerializer());
45
46 template.setHashKeySerializer(new StringRedisSerializer());
47 template.setHashValueSerializer(new StringRedisSerializer());
48 template.afterPropertiesSet();
49
50 return template;
51 }
52
53 }
RedisConfig
再次redis中查看缓存对象:redis中中文乱码,但是存取没影响

192.168.6.3:6379> keys *

  1. “city_2”
    192.168.6.3:6379> get city_2
    “[“com.redis.springbootredis.pojo.City”,{“id”:2,“name”:”\xe5\xa4\xa9\xe6\xb4\xa5",“description”:"\xe6\xac\xa2\xe8\xbf\x8e\xe4\xbd\xa0\xe6\x9d\xa5\xe6\x8c\xa4\xe5\x9c\xb0\xe9\x93\x81",“createDate”:[“java.sql.Timestamp”,1557737066000]}]"
    换个姿势再来一次

如何在get时取到它的中文呢?只需要在redis-cli 后面加上 –raw

192.168.6.3:6379> QUIT
[root@localhost src]# ./redis-cli -h 192.168.6.3 -p 6379 --raw
192.168.6.3:6379> keys *
city_2
192.168.6.3:6379> get city_2
[“com.redis.springbootredis.pojo.City”,{“id”:2,“name”:“天津”,“description”:“欢迎你来挤地铁”,“createDate”:[“java.sql.Timestamp”,1557737066000]}]

参考链接

<think>好的,我现在需要解决用户在Spring Boot中集成Redis存储对象的问题。首先,我应该回顾一下Spring Boot和Redis整合的基本知识。记得Spring Data Redis提供了RedisTemplate和StringRedisTemplate来操作Redis,而存储对象需要序列化。用户提到存储对象,所以关键点应该是如何正确配置序列化器。 接下来,我需要考虑用户可能的背景。他们可能已经知道如何集成Redis,但不确定如何处理对象。因此,我应该从基础配置开始,然后逐步引入序列化的配置。 首先,添加依赖项是必要的。Spring Boot Starter Data Redis会引入必要的库,比如Lettuce或者Jedis。默认可能是Lettuce,所以需要提到这一点。然后,配置连接信息,比如在application.properties中设置host、port等。 接下来,配置RedisTemplate。默认情况下,RedisTemplate使用JdkSerializationRedisSerializer,但这可能会导致存储的键有乱码前缀,比如\xac\xed\x00。所以建议更换为Jackson2JsonRedisSerializer或者GenericJackson2JsonRedisSerializer,这样存储的是JSON格式,可读性更好。 需要创建一个配置类,比如RedisConfig,通过@Bean定义RedisTemplate,并设置key和value的序列化器。同时,可能需要配置连接工厂,特别是如果使用哨兵或集群模式的话,但用户的问题可能只需要单机配置。 然后,编写一个服务类,使用@Autowired注入RedisTemplate,并实现存储和获取对象的方法。这里需要注意,使用opsForValue()来操作字符串类型的值,但存储对象时,序列化器会自动处理转换。 测试部分,可以通过编写一个Controller或者单元测试来验证存储和读取是否正常。确保对象被正确序列化和反序列化,尤其是日期、嵌套对象等复杂结构。 可能遇到的问题包括:序列化器配置错误导致无法转换对象,依赖缺失,或者Redis连接配置不正确。需要提醒用户检查这些点。 最后,根据用户提供的引用,引用[1]提到了Spring Boot Redis示例和源码下载,引用[2]涉及哨兵模式的配置,但用户的问题可能不需要哨兵,所以主要参考引用[1]的内容。 现在,整理这些步骤,确保结构清晰,代码示例正确,并符合用户要求的LaTeX格式和引用标识。</think>### Spring Boot集成Redis存储对象教程 #### 1. 添加依赖 在`pom.xml`中添加Spring Data Redis和JSON序列化依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> ``` Spring Boot默认使用Lettuce客户端连接Redis[^1]。 #### 2. 配置Redis连接 在`application.properties`中配置连接信息: ```properties spring.redis.host=localhost spring.redis.port=6379 # 若需要密码 spring.redis.password=yourpassword ``` #### 3. 配置序列化器 创建配置类`RedisConfig.java`,定义支持对象存储RedisTemplate: ```java @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 使用JSON序列化 Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); return template; } } ``` 此配置将对象存储为JSON格式,避免默认JDK序列化的二进制不可读问题。 #### 4. 实现存储逻辑 创建服务类`UserService.java`: ```java @Service public class UserService { @Autowired private RedisTemplate<String, Object> redisTemplate; public void saveUser(String key, User user) { redisTemplate.opsForValue().set(key, user); } public User getUser(String key) { return (User) redisTemplate.opsForValue().get(key); } } ``` 其中`User`为自定义对象: ```java public class User implements Serializable { private String name; private Integer age; // 必须包含无参构造器和getter/setter } ``` #### 5. 测试验证 在Controller或单元测试中调用: ```java @RestController public class DemoController { @Autowired private UserService userService; @GetMapping("/test") public String test() { User user = new User(); user.setName("Alice"); user.setAge(28); userService.saveUser("user:1001", user); return "存储成功"; } } ``` 通过Redis CLI验证存储结果: ``` 127.0.0.1:6379> GET "user:1001" "{\"@class\":\"com.example.User\",\"name\":\"Alice\",\"age\":28}" ``` #### 关键注意事项 1. **序列化要求**:存储对象需实现`Serializable`接口,推荐使用JSON序列化提升可读性 2. **类型保留**:使用`Jackson2JsonRedisSerializer`时默认添加`@class`类型信息,可通过配置`ObjectMapper`禁用 3. **性能优化**:复杂对象建议配置过期时间:`redisTemplate.expire(key, 300, TimeUnit.SECONDS)`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值