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

升级Spring Boot3.2.0后,@Lock乐观锁触发非版本实体版本递增断言错误

问题描述

我有一个带版本控制的实体,从数据库读取时通过@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)实现乐观锁。在Spring Boot 2.7.11版本下一切正常,冲突时会抛出ObjectOptimisticLockingFailureException;但升级到Spring Boot 3.2.0(对应Hibernate 6.3.1)后,执行查询时却抛出org.hibernate.AssertionFailure: cannot force version increment on non-versioned entity错误。

实体类定义:

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.annotations.CreationTimestamp;

import jakarta.persistence.*;
import java.util.Collection;
import java.util.Date;

@Entity
public class Case {

    @Version
    private int version;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    @JoinColumn(nullable = false)
    private Merchant merchant;

    @Column(name = "case_creation_date", nullable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @CreationTimestamp
    private Date caseCreationDate;

    @ManyToOne
    @JsonIgnore
    private Agent agent;

    @Column(length = 2000, nullable = true)
    private String note;

    @ManyToMany
    @JoinTable(name = "case_justification")
    private Collection<Justification> caseResolutionJustifications;

    // constructors

    // setters and getters

}

查询Repository代码:

@Repository
public interface CasesRepository extends JpaRepository<Case, Long> {

    @Transactional
    @Override
    @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
    Optional<Case> findById(Long caseId);
}

尝试过删除数据库表让Hibernate重新生成,问题依旧。

原因分析

Hibernate 6.x系列对乐观锁强制增量的校验逻辑做了强化,相比5.x(对应Spring Boot 2.x)更严格:

  • 当在Repository方法上直接标注@Lock(OPTIMISTIC_FORCE_INCREMENT)时,Hibernate可能在实体元数据初始化阶段,无法正确识别@Version字段的存在(尤其是字段访问权限、类型初始化的细节)。
  • 另外,int类型的@Version字段默认值为0,Hibernate 6.x可能会将其视为未初始化的版本状态,进而判定实体无版本控制。
解决方案

方案1:调整@Version字段类型

将@Version字段的类型从int改为Integer,避免默认值0被Hibernate误判为未初始化:

@Version
private Integer version;

方案2:在Service层手动触发锁操作

去掉Repository方法上的@Lock注解,改为在Service层获取实体后,通过EntityManager手动执行锁操作:

@Service
public class CaseService {

    @Autowired
    private CasesRepository casesRepository;

    @Autowired
    private EntityManager entityManager;

    @Transactional
    public Optional<Case> findByIdWithOptimisticLock(Long caseId) {
        Optional<Case> caseOpt = casesRepository.findById(caseId);
        caseOpt.ifPresent(caseEntity -> 
            entityManager.lock(caseEntity, LockModeType.OPTIMISTIC_FORCE_INCREMENT)
        );
        return caseOpt;
    }
}

方案3:确保@Version字段的可访问性

检查version字段的getter/setter是否正确实现,确保Hibernate能通过反射访问到该字段。如果是字段访问策略(默认),可以尝试将version字段改为protected或public,或者显式指定@Access(AccessType.FIELD):

@Entity
@Access(AccessType.FIELD)
public class Case {
    @Version
    private int version;
    // ...其他字段
}

方案4:使用Hibernate特定的锁注解替代

尝试使用Hibernate的@OptimisticLocking注解配合FORCE_INCREMENT策略,在实体类上补充配置:

@Entity
@OptimisticLocking(type = OptimisticLockType.FORCE_INCREMENT)
public class Case {
    @Version
    private int version;
    // ...其他字段
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:09:54