You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot3.2+Hibernate6下Spring Cache缓存/反序列化Hibernate代理方案

升级Spring Boot 3.2.0后Spring Cache反序列化Hibernate懒加载代理异常解决

问题现象

升级至Spring Boot 3.2.0后,Spring Cache出现反序列化异常:缓存的JPA实体X包含即时加载的ManyToOne关联实体O,O又关联懒加载实体P。P被序列化为org.hibernate.proxy.pojo.bytebuddy.SerializableProxy,从缓存读取时抛出java.lang.IllegalStateException: Could not identify any active SessionFactory having UUID ...异常,但该逻辑在Spring Boot 2.7/Hibernate 5.6环境下运行正常。

原因分析

Hibernate 6对SerializableProxy类做了修改:新增sessionFactoryUuid成员变量,其readResolve方法在反序列化时会通过该UUID查找对应的SessionFactory。但缓存中保存的代理对象携带的UUID,与当前运行环境的SessionFactory UUID无法匹配,导致查找失败。而Hibernate 5.6的readResolve方法并未依赖SessionFactory UUID来完成反序列化。

新旧版本readResolve代码对比

旧代码(Spring Boot 2.7/Hibernate 5.6):

private Object readResolve() {
  BytecodeProvider bytecodeProvider = Environment.getBytecodeProvider();
  if ( !( bytecodeProvider instanceof BytecodeProviderImpl ) ) {
    throw new IllegalStateException( "The bytecode provider is not ByteBuddy, unable to deserialize a ByteBuddy proxy." );
  }
  HibernateProxy proxy = ( (BytecodeProviderImpl) bytecodeProvider ).getByteBuddyProxyHelper().deserializeProxy( this );
  afterDeserialization( (ByteBuddyInterceptor) proxy.getHibernateLazyInitializer() );
  return proxy;
}

新代码(Spring Boot 3.2/Hibernate 6.4.1):

private Object readResolve() {
  final SessionFactoryImplementor sessionFactory = retrieveMatchingSessionFactory( this.sessionFactoryUuid, this.sessionFactoryName );
  BytecodeProviderImpl byteBuddyBytecodeProvider = retrieveByteBuddyBytecodeProvider( sessionFactory );
  HibernateProxy proxy = byteBuddyBytecodeProvider.getByteBuddyProxyHelper().deserializeProxy( this );
  afterDeserialization( (ByteBuddyInterceptor) proxy.getHibernateLazyInitializer() );
  return proxy;
}

解决方案

临时方案(不推荐)

缓存前手动取消实体代理(如调用Hibernate.unproxy()),但该操作会破坏懒加载设计,强制加载关联实体,额外消耗系统资源,仅适合临时应急。

最优配置方案

通过配置Hibernate SessionFactory名称并禁用JNDI查找,让反序列化逻辑通过名称匹配SessionFactory,替代UUID匹配:
在application.properties中添加以下配置:

hibernate.session_factory_name=custom-hibernate-session
hibernate.session_factory_name_is_jndi=false

或在application.yml中配置:

hibernate:
  session_factory_name: custom-hibernate-session
  session_factory_name_is_jndi: false

最佳实践

  • 优先缓存DTO而非JPA实体:将业务数据转换为DTO(数据传输对象)后再缓存,完全规避懒加载代理的序列化/反序列化问题,同时符合领域模型与数据传输分离的设计原则。
  • 控制JPA实体序列化行为:若必须缓存JPA实体,可使用Jackson的Hibernate 6模块(jackson-datatype-hibernate6),配置序列化时自动取消代理或忽略未加载的关联字段。
  • 分布式环境下保持SessionFactory名称一致:在多节点部署场景中,确保所有节点的hibernate.session_factory_name配置相同,避免因名称不匹配导致反序列化失败。

内容的提问来源于stack exchange,提问作者Yury K.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 01:13:24