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

升级Hibernate 6后L2缓存中复合ID关联懒加载字段异常求助

Hibernate 6 L2缓存异常:复合ID包含懒加载@ManyToOne关联字段时读取缓存报错

环境:

  • 数据库:PostgreSQL 14.5、H2 2.1.214
  • 框架版本:从Hibernate 5.6.15升级至Hibernate 6.1.7~6.2.3、Spring Data JPA 3.0.6、Spring Boot 3.0.7
  • 缓存:Ehcache 3.10.8
  • JDK:OpenJDK Temurin 17.0.6

实体定义

Product 实体

@Getter
@IdClass(ProductPK.class)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
@NoArgsConstructor(access = PROTECTED)
@Entity
@OptimisticLocking(type = OptimisticLockType.ALL)
@DynamicUpdate
@Cacheable
@Cache(usage = READ_WRITE)
@Table(name = "PRODUCTS")
public class Product {
    public Product(String productId, Operator operator) {
        this.productId = productId;
        this.operator = operator;
    }

    @EqualsAndHashCode.Include
    @ToString.Include
    @Id
    @Column(name = "PRODUCT_ID", nullable = false)
    private String productId;
    @Id
    @EqualsAndHashCode.Include
    @ToString.Include
    @Getter
    @Setter
    @ManyToOne(fetch = LAZY)
    @JoinColumn
    private Operator operator;

    @Column(name = "DESCRIPTION")
    @Setter
    private String description;

    @AllArgsConstructor
    @EqualsAndHashCode
    @Getter
    @Setter
    @NoArgsConstructor(access = PRIVATE)
    public static class ProductPK implements Serializable {
        private String productId;
        private String operator;
    }
}

Operator 实体

@Getter
@Entity
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor(access = PROTECTED)
@Table(name = "OPERATORS")
@OptimisticLocking(type = OptimisticLockType.DIRTY)
@DynamicUpdate
@Cacheable
@Cache(usage = READ_WRITE)
public class Operator {

    public Operator(String operatorId) {
        this.operatorId = operatorId;
    }

    @EqualsAndHashCode.Include
    @ToString.Include
    @Id
    @Column(name = "OPERATOR_ID", nullable = false)
    private String operatorId;

    @OneToMany(mappedBy = "operator", cascade = {CascadeType.ALL}, orphanRemoval = true)
    private List<Product> products = new ArrayList<>();

    public void setProducts(List<Product> products) {
        this.products = products;
    }
}

测试用例及异常

测试1

连续两次调用带有@Transactional(propagation = REQUIRES_NEW)的getProduct方法,第二次从L2缓存读取时抛出异常:

Caused by: org.hibernate.HibernateException: identifier of an instance of com.example.demo.local.Product was altered from com.example.demo.local.Product$ProductPK@226fd to com.example.demo.local.Product$ProductPK@44bf91

测试2

首次读取后,在带有@Transactional(readOnly = true)的readProduct方法中从缓存读取时抛出空指针异常,Hibernate似乎未识别operator为懒加载字段:

java.lang.NullPointerException: Cannot invoke "com.example.demo.local.Operator.getOperatorId()" because the return value of "com.example.demo.local.Product.getOperator()" is null

已在Hibernate官方仓库提交问题(HHH-16673),目前暂未收到回复,现寻求可行的解决方法或临时规避方案。

内容的提问来源于stack exchange,提问作者BummyWatermoon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 13:27:47