服务器重启后插入数据触发唯一约束违反问题排查
嘿,这个问题我之前维护JBoss EAP + Oracle的项目时也踩过同款坑!核心原因很直白:服务器重启后,Hibernate生成的主键值和数据库里已存在的主键重复了,直接触发了Oracle的ORA-00001唯一约束报错。结合你首次启动正常、重启就炸的场景,大概率是主键生成策略的配置或者序列的状态出了问题,咱们一步步来解决:
1. 先盯紧主键生成策略的配置
先检查你的实体类主键注解怎么写的——如果用的是Oracle序列,最容易踩的坑就是allocationSize和数据库序列的步长不匹配,或者没显式指定序列导致Hibernate自动生成的序列出问题。
正确的序列配置姿势
如果是用Oracle原生序列,一定要显式配置,别让Hibernate瞎猜:
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq") @SequenceGenerator( name = "user_seq", sequenceName = "SCHEMA.USER_SEQ", // 对应Oracle里你创建的序列名 allocationSize = 10, // 这个必须和序列的INCREMENT BY值完全一致!划重点! initialValue = 1 ) private Long id;
这里的allocationSize是Hibernate用来缓存序列值的步长,如果和Oracle序列的INCREMENT BY不一样,重启后Hibernate的缓存序列段就会和数据库实际的序列值脱节,分分钟生成重复主键。
2. 检查Oracle序列的当前状态
登录Oracle数据库,先查序列的当前值:
SELECT last_number FROM user_sequences WHERE sequence_name = 'USER_SEQ';
再查对应表的最大主键值:
SELECT MAX(id) FROM SCHEMA.USER_TABLE;
如果序列的last_number小于等于表中的最大主键值,那重启后Hibernate用这个序列生成的主键肯定撞车!解决办法是把序列的当前值调得比表中最大主键大,比如:
-- 先把步长设大,跳一大步 ALTER SEQUENCE SCHEMA.USER_SEQ INCREMENT BY 100; -- 触发序列更新 SELECT USER_SEQ.NEXTVAL FROM DUAL; -- 改回原来的步长 ALTER SEQUENCE SCHEMA.USER_SEQ INCREMENT BY 1;
3. 排查Hibernate的旧版生成器缓存问题
如果你的项目用了比较老的Hibernate配置(比如hibernate.id.new_generator_mappings=false),旧版的序列生成器缓存机制容易在重启后出问题。建议:
- 确保用的是Hibernate 5+的新版生成器(默认
hibernate.id.new_generator_mappings=true) - 如果之前用的是
TABLE类型的主键生成策略,赶紧换成SEQUENCE——Oracle原生支持序列,性能更好,也不容易出现这种重启后主键冲突的问题
4. 确认DDL自动生成配置没搞鬼
最后检查下你的JPA配置,别犯低级错误:
# 生产环境绝对不能用create/create-drop!会删表重建! hibernate.hbm2ddl.auto=update
如果是create或者create-drop,重启时会删掉原有表重建,但你说首次启动正常,重启报错,这个概率不大,但还是确认下更稳妥。
快速验证方案
先手动把序列的当前值调到比表中最大主键大,然后重启服务器测试。如果问题解决了,那就是序列和实际数据不匹配的问题,再把配置里的allocationSize和序列步长对齐就搞定了。
内容的提问来源于stack exchange,提问作者devil0150

