使用Hibernate SEQUENCE生成ID时出现唯一约束违反的原因
嘿,这个问题我之前帮同事排查过类似的,结合你的代码和场景,大概率是下面这几个原因之一:
数据库序列与Hibernate状态不同步
虽然你设置了allocationSize=1,但如果之前调整过这个参数(比如曾经设为10后来改回1),Hibernate内部可能还残留了缓存的ID批次。当你删除实体后,Hibernate尝试用这些缓存的旧ID插入,而这些ID可能已经被数据库中剩余的记录占用,直接触发唯一约束。另外,如果有人直接操作数据库修改过ParamSequence的当前值(比如手动执行ALTER SEQUENCE ParamSequence RESTART WITH ...),也会导致Hibernate生成的ID和数据库序列实际值不匹配。数据库序列的单向特性+外部操作干扰
大部分数据库的序列是单向递增的,删除实体并不会让序列回退。比如你之前生成过ID 1、2、3,删除ID 2后,序列会继续生成4,这本来没问题,但如果有人绕过Hibernate手动插入了ID 2的记录,或者把序列重置到了小于当前最大ID的值,那你插入新记录时就会和现有ID冲突。会话缓存未及时刷新
如果删除实体和插入新记录在同一个Hibernate Session中执行,而且没有手动调用flush()或clear()刷新会话缓存,Hibernate可能没有同步数据库的最新状态,导致生成的ID和数据库中实际存在的ID重复。比如删除ID=5后,会话缓存还保留着相关状态,插入时错误地复用了这个ID。数据库序列的缓存设置冲突
有些数据库(比如PostgreSQL)的序列默认会开启缓存机制(比如CACHE 10),哪怕你在Hibernate里设置了allocationSize=1,数据库层面还是会预存一批序列值。如果你的应用有多个节点,不同节点可能拿到重复的预存ID,删除操作后插入就会触发唯一约束。这种情况需要检查数据库序列的缓存配置,确保和Hibernate的allocationSize一致。手动赋值ID覆盖了序列生成值
排查下代码里有没有地方手动给Param的ID字段赋值(比如param.setId(xxx)),如果这个手动设置的ID已经存在于数据库中,哪怕Hibernate的序列生成器准备生成新ID,手动赋值也会覆盖它,直接导致唯一约束违反。
内容的提问来源于stack exchange,提问作者Salih Erikci

