SpringBoot3/Hibernate6多实例下Envers版本访问问题
官方推荐解决方案
针对Spring Boot 3(Hibernate 6)中Envers版本号因序列预分配导致的多实例乱序问题,官方推荐以下几种针对性解决方案:
1. 为Envers配置独立的序列生成器
将Envers的版本号序列与业务实体序列分离,单独设置Envers序列的分配大小为1,既保证版本号严格递增,又不影响业务实体的序列性能:
在application.properties中添加配置:
# 指定Envers版本序列名称 spring.jpa.properties.org.hibernate.envers.revision_sequence_name=REV_SEQ # 设置Envers序列分配大小为1 spring.jpa.properties.org.hibernate.envers.revision_sequence_allocation_size=1
业务实体的序列可保持默认的50分配大小,避免全局修改带来的性能损耗。
2. 切换为基于表的版本生成器
使用数据库表来生成全局唯一的版本号,天然避免序列预分配的乱序问题,适合对版本号有序性要求极高的场景:
# 指定版本生成器类型为表 spring.jpa.properties.org.hibernate.envers.revision_generator=table # 表生成器配置 spring.jpa.properties.org.hibernate.envers.revision_table_generator_table=REV_GENERATOR spring.jpa.properties.org.hibernate.envers.revision_table_generator_segment_column=SEGMENT spring.jpa.properties.org.hibernate.envers.revision_table_generator_segment_value=DEFAULT spring.jpa.properties.org.hibernate.envers.revision_table_generator_value_column=NEXT_VAL
该方案通过数据库行锁保证版本号唯一性,性能略低于序列生成器,但无乱序风险。
3. 自定义Revision实体实现时间戳排序
若需保留现有序列配置,可通过扩展REVINFO实体,利用REVTSTMP字段实现按时间戳排序查询:
- 创建自定义Revision实体:
@Entity @RevisionEntity(CustomRevisionListener.class) public class CustomRevisionEntity extends DefaultRevisionEntity { // 可添加自定义字段,或直接复用父类的revtstmp字段 }
- 实现RevisionListener:
public class CustomRevisionListener implements RevisionListener { @Override public void newRevision(Object revisionEntity) { CustomRevisionEntity revision = (CustomRevisionEntity) revisionEntity; // 可在此设置额外属性,如实例标识等 } }
- 查询时按时间戳排序:
AuditReader reader = AuditReaderFactory.get(entityManager); List<Object[]> revisions = reader.createQuery() .forRevisionsOfEntity(YourEntity.class, false, true) .addOrder(AuditEntity.revisionProperty("revtstmp").asc()) .getResultList();
这种方式通过时间戳保证查询结果的顺序正确性,无需修改序列分配规则。
注意事项
不推荐直接全局修改allocation_size为1,这会导致所有业务实体频繁查询数据库序列,在高并发场景下严重影响性能,官方更建议采用上述针对性配置方案。
内容的提问来源于stack exchange,提问作者GJohannes
相关产品推荐
相关产品推荐

