Hibernate批量更新下optimistic_force_increment版本自增失效问题
这个问题确实是Hibernate 6.x(包括你使用的6.2.0.Final)中,批量更新机制与OPTIMISTIC_FORCE_INCREMENT锁结合时的已知问题。当事务内未修改任何实体,仅通过OPTIMISTIC_FORCE_INCREMENT触发版本自增时,生成的版本更新SQL会被加入批量队列,但在事务提交的beforeCompletion阶段不会被执行,导致版本未实际自增,同时出现HHH100503警告。
可行解决方案(无需关闭全局批量更新)
1. 事务内手动触发flush
在使用OPTIMISTIC_FORCE_INCREMENT查询实体后,手动调用EntityManager.flush(),强制Hibernate将批量队列中的SQL发送到数据库。修改你的测试方法事务块:
transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status){ EntityManager em = entityManagerFactory.createEntityManager(); try { calendarRepository .findBySystemCodeAndNameWithOptimisticForceIncrement("AAA", "Calendar for AAA") .orElseThrow(); em.flush(); // 手动触发批量执行 } finally { em.close(); } } });
如果使用Spring Data JPA,也可以直接在Repository方法上配合@Modifying实现flush,或者在服务层注入EntityManager调用flush。
2. 针对特定实体禁用批量更新
在Calendar实体类上添加@BatchSize(size = 1)注解,让该实体的更新操作不参与批量处理,其他实体仍能正常使用批量更新:
@Entity @Table(name = "calendar") @BatchSize(size = 1) // 禁用该实体的批量更新 public class Calendar implements Serializable { // ... 原有实体代码 }
3. 调整Hibernate全局配置
添加配置hibernate.jdbc.batch_versioned_data=false,该参数会让所有版本化实体的更新语句跳过批量处理,单独执行。虽然会影响版本更新的批量性能,但能彻底解决这类问题,适合对版本一致性要求高于批量性能的场景。
原因补充
你调试的结论完全正确:Hibernate在beforeCompletion阶段处理OPTIMISTIC_FORCE_INCREMENT的版本自增时,若开启批量更新,会使用MutationExecutorSingleBatched将SQL加入队列,但这个阶段不会触发批量执行(批量执行通常在事务提交的主流程中处理),导致队列中的SQL被遗留,最终版本未更新。
内容的提问来源于stack exchange,提问作者JayVeeInCorp

