Liquibase在Oracle环境执行SQL报ORA-00001唯一约束冲突排查
Liquibase执行Oracle插入语句触发唯一约束冲突的可能成因
- 约束归属定位的天然误导:Oracle抛出
ORA-00001类异常时,仅返回约束本身的名称,不会主动标注约束所属的表对象。本次报错中的PKH_SYSTEM_MESSAGE虽然命名上和主表SYSTEM_MESSAGE强关联,但实际是创建在历史表SYSTEM_MESSAGE_H上的约束;初始排查仅聚焦为空的主表本身,完全偏离了冲突的实际来源。 - 历史表快照残留引发的键值冲突:
- 开发者最初在数据库客户端直接执行相同插入语句时,由于当时历史表无对应键值记录,触发器同步写入历史表的操作可以正常完成。后续为了复现Liquibase的报错,仅清空了主表
SYSTEM_MESSAGE的数据,没有同步清除历史表SYSTEM_MESSAGE_H中已经存在的、匹配CODE='mi.board' + SYS_VERSION=0规则的快照记录。 - 当Liquibase再次执行主表插入操作时,
THI_SYSTEM_MESSAGE行级触发器会自动生成键值完全一致的快照写入历史表,直接命中历史表的唯一约束,触发冲突。
- 开发者最初在数据库客户端直接执行相同插入语句时,由于当时历史表无对应键值记录,触发器同步写入历史表的操作可以正常完成。后续为了复现Liquibase的报错,仅清空了主表
- 调试过程中的事务残留:
- 前期调试该changeSet的阶段,只要执行链路走到BEFORE INSERT触发器环节,就会触发向历史表的写入动作;如果后续因为changeSet校验失败、Liquibase写入元数据表
DATABASECHANGELOG超时等非数据库操作本身的问题触发事务回滚,当历史表的写入逻辑配置了独立的事务提交机制时,历史表的写入不会随主表操作一同回滚,也会导致历史表残留对应的重复键值,后续正常执行插入时就会触发约束冲突。
- 前期调试该changeSet的阶段,只要执行链路走到BEFORE INSERT触发器环节,就会触发向历史表的写入动作;如果后续因为changeSet校验失败、Liquibase写入元数据表
- 配置一致性强化的误判:
- 本次配置使用
<sql>标签时开启了Oracle专属的分语句拆分、自定义分隔符配置,和Liquibase原生<insert>标签的执行逻辑完全一致;不管采用哪种标签形式,只要走到触发器写入历史表的链路,遇到历史表的残留记录就会触发相同的报错,进一步加深了“约束挂载在主表上”的初始错误判断。
- 本次配置使用
内容的提问来源于stack exchange,提问作者Markus Ridziauskas
相关产品推荐
相关产品推荐

