升级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

