Quarkus 3.2.0.Final迁移后序列同步异常与主键重复问题求助
问题根因分析与解决建议
根因分析
1. 序列命名问题
Quarkus 3.x升级到了Hibernate 6,而Hibernate 6对Envers的默认序列命名策略做了修改:
- Hibernate 5(Quarkus 2.x)中Envers默认使用的序列名为
info_rev_seq - Hibernate 6中改为
revinfo_seq
这就是你遇到第一个报错“找不到public.revinfo_seq”的原因,重命名序列后该问题解决,但也为后续主键冲突埋下伏笔。
2. 主键重复与序列失同步问题
核心原因是Hibernate 6中Envers的REVINFO主键生成策略发生了变更:
- 在Hibernate 5中,Envers默认依赖PostgreSQL
bigserial类型的IDENTITY生成策略,由数据库自动维护序列与表的主键同步 - Hibernate 6中,Envers默认切换为显式SEQUENCE生成策略,直接操作指定序列生成revision编号
- 你将原序列
info_rev_seq重命名为revinfo_seq后,该序列的当前值远低于REVINFO表中已有的最大rev值(23 vs 54),导致Envers生成的revision编号与表中已存在的主键重复 - 手动同步序列后再次失同步,大概率是因为部分场景下(如事务回滚、批量审计操作),Envers的序列使用逻辑与数据库序列的原子性生成逻辑不匹配,或者配置中存在
allocationSize大于1的情况,导致序列预分配的编号未被全部使用,后续再次生成时出现冲突。
解决建议
1. 强制Envers使用IDENTITY主键生成策略
如果希望继续依赖PostgreSQL bigserial的自动维护逻辑,在application.properties中添加配置:
hibernate.envers.revision_generator.strategy=identity hibernate.envers.use_revision_entity_with_native_id=true
该配置会让Envers回到Hibernate 5的行为,直接使用数据库原生的IDENTITY生成主键,无需手动维护序列。
2. 修复序列与表的同步并配置正确的序列策略
如果坚持使用SEQUENCE策略:
- 首先执行SQL将序列值同步到表中最大
rev+1(避免立即冲突):
SELECT setval('public.revinfo_seq', (SELECT COALESCE(MAX(rev), 0) + 1 FROM public.revinfo));
- 然后在
application.properties中明确配置序列相关参数:
hibernate.envers.revision_generator.strategy=sequence hibernate.envers.revision_generator.sequence_name=revinfo_seq # 分配大小设为1,避免预分配导致的序列浪费与失同步 hibernate.envers.revision_generator.allocation_size=1
- 如果自定义了
RevisionEntity,需要在@RevisionNumber字段上显式指定序列生成器:
@RevisionEntity public class CustomRevisionEntity extends DefaultRevisionEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "revinfo_seq_gen") @SequenceGenerator( name = "revinfo_seq_gen", sequenceName = "revinfo_seq", allocationSize = 1 ) @RevisionNumber private long rev; // 其他字段与逻辑 }
3. 排查事务回滚场景
检查系统中是否存在大量包含审计操作的事务回滚场景:PostgreSQL的序列值在事务回滚时不会回滚,如果Envers在事务中预取了序列值但最终事务回滚,会导致序列值与表中主键出现偏差。这种情况下,要么调整业务逻辑减少不必要的回滚,要么使用IDENTITY策略规避序列预分配的问题。
内容的提问来源于stack exchange,提问作者Fortran
相关产品推荐
相关产品推荐

