Spring Boot Redis缓存JSON序列化读取失败:No session求助
问题分析与解决思路
这个报错的核心是JPA懒加载机制和JSON序列化/反序列化的冲突:
- 用
@Cacheable缓存带关联列表的实体时,JPA默认关联关系是懒加载(fetch = FetchType.LAZY),序列化JSON时关联列表只是未初始化的代理对象; - 从Redis反序列化读取缓存时,代理对象尝试初始化关联数据,但此时已经没有Hibernate Session上下文,直接抛出
No session错误。
具体解决方案
1. 临时方案:改为急加载
把实体中关联列表的fetch属性改为FetchType.EAGER,查询时直接加载关联数据,序列化到Redis的是完整数据,反序列化时不会触发懒加载:
@OneToMany(fetch = FetchType.EAGER) private List<Entity2> entityList;
注意:若关联数据量较大,会拖慢查询性能,仅适合小数据场景。
2. 推荐方案:序列化前手动加载懒加载数据
在Service层查询到实体后,先初始化关联列表再返回(@Cacheable会缓存方法返回值):
@Cacheable(value = "entity1Cache") public Entity1 getEntity1ById(Long id) { Entity1 entity1 = entity1Repository.findById(id).orElse(null); if (entity1 != null) { Hibernate.initialize(entity1.getEntityList()); // 手动触发懒加载,加载关联数据 } return entity1; }
3. 优雅方案:配置Jackson自动处理Hibernate懒加载
添加Jackson的Hibernate支持依赖:
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate5</artifactId> </dependency>
然后在Redis序列化配置中注册该模块,让Jackson序列化时自动处理懒加载代理:
@Configuration public class RedisConfig { @Bean public RedisCacheConfiguration redisCacheConfiguration() { ObjectMapper objectMapper = new ObjectMapper() .registerModule(new Hibernate5Module()); // 注册Hibernate模块,自动处理懒加载 return RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer(objectMapper) )); } }
如果需要强制加载所有懒加载数据,可以给模块添加配置:new Hibernate5Module().enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING)
4. 最佳实践:缓存DTO而非JPA实体
定义只包含业务所需字段的DTO(数据传输对象),查询后将实体转换为DTO再缓存,彻底规避懒加载问题:
// DTO类 public class Entity1DTO { private Long id; private String name; private List<Entity2DTO> entityList; // getter、setter、构造方法 } // Service层代码 @Cacheable(value = "entity1Cache") public Entity1DTO getEntity1DTOById(Long id) { Entity1 entity1 = entity1Repository.findById(id).orElse(null); if (entity1 == null) return null; // 实体转DTO(可使用MapStruct等工具简化转换逻辑) Entity1DTO dto = new Entity1DTO(); dto.setId(entity1.getId()); dto.setName(entity1.getName()); dto.setEntityList(entity1.getEntityList().stream() .map(e2 -> { Entity2DTO e2Dto = new Entity2DTO(); e2Dto.setId(e2.getId()); e2Dto.setContent(e2.getContent()); return e2Dto; }).collect(Collectors.toList())); return dto; }
5. 检查Redis序列化配置
确保Redis使用正确的JSON序列化器,示例配置:
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(jsonSerializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(jsonSerializer); return template; } @Bean public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(connectionFactory) .cacheDefaults(config) .build(); } }
内容的提问来源于stack exchange,提问作者Draaksward
相关产品推荐
相关产品推荐

