在集群模式下添加子表的行时 H2 参照完整性违规

Posted

技术标签:

【中文标题】在集群模式下添加子表的行时 H2 参照完整性违规【英文标题】:H2 Referential Integrity violation when adding row of a child table in cluster mode 【发布时间】:2015-10-21 09:34:05 【问题描述】:

TL; DR:未发生违规时触发参照完整性违规。 我在具有 2 个节点的集群模式下运行 H2。 我在 H2 数据库(v1.4.189)中有两个表,一个父级和一个子级。子表包含指向父表行 ID 的外键。 通常,在子表中插入一行时我不会收到任何错误。 但是过了一会儿,我在插入时遇到了这个错误:

Referential integrity constraint violation: "CONSTRAINT_1FE: PUBLIC.CHILD FOREIGN KEY(fkey)
REFERENCES PUBLIC.PARENT(ID) (86)"

奇怪的是,产生错误的INSERT INTO数据被成功插入,而且没有违反外键约束!

我已尝试记录重现错误的确切步骤,但使用新数据库时,该错误永远不会发生:

drop table CHILD;
drop table PARENT;
create table CHILD(id int auto_increment, name varchar(255), fkey int);
create table PARENT(id int auto_increment, name varchar(255));

ALTER TABLE `CHILD` ADD FOREIGN KEY (fkey) REFERENCES `PARENT` (`id`);

insert into PARENT(name) values('hello');
insert into PARENT(name) values('world');
select * from PARENT; 


insert into CHILD(name, fkey) values('hello', 1); 
-- this works for a while, but someday the Referential integrity error 
-- will pop, but data will be added anyway (wtf?)
insert into CHILD(name, fkey) values('world', 2);

在数据库上,我只做简单的事情,比如选择、插入、删除……

有趣的事实是,在这个错误发生一次之后,我得到了另一个奇怪的错误:当删除(或更新)CHILD 表的行时,DELETE FROM 或 UPDATE 函数总是返回 0,即使某些行已被删除。 ..(同样 jdbc executeUpdate() 总是返回 0)

数据库是否在某个时候损坏了?

我发现修复此错误的唯一解决方法是删除所有表并重新创建表,这不是我想要做的。

【问题讨论】:

很可能auto_increment 生成的值与您在子表的插入语句中硬编码的值不同。 这不是问题。我提供了这些步骤来帮助理解问题。无论如何,在我的应用程序中,我总是在插入子表之前检查外键值是否存在。 不相关,但是:“我总是在插入之前检查外键值是否存在” - 这完全是浪费时间,因为这正是 FK 约束的用途.您应该只处理错误,而不是在代码中重复 FK 约束检查。但我更倾向于相信 H2 而不是你声称你的代码没问题。我能想到的唯一可能的事情:H2 V1.4 仍被标记为“beta”版本。你试过 V1.3 吗? 你完全正确!我对这个“检查”进行了编码,因为我首先遇到了所描述的错误,并且它提供了一种解决方法(如果检查正常 + 违反参照完整性 = 一切都很好)。有趣的是,现在我已经删除了所有表,并重新创建了数据库的结构(与以前完全相同),我无法重现该问题(v1.3 都没有测试)... YET !跨度> 当您有minimal reproducible example 会重现您的问题时,请edit 您的问题。没有它,任何人都可以做的就是猜测您的问题可能是什么。 【参考方案1】:

H2 主开发者 thomas mueller 在 github 上回答了这个问题:

这是集群功能的记录限制之一,请参阅 还有“Clustering Algorithm and Limitations”:“Using auto-increment and 当前不支持标识列。”

恐怕很难修复它。我建议不要使用集群 出于这个原因的功能。对它的支持可能会在 未来。我希望一个新的集群/自动故障转移功能可以 已添加,但这需要一些时间。

但这对你来说可能很有趣:https://github.com/shesse/h2ha

见issue on github

我设法让它工作:使用序列并插入两次: - 获取此表序列的 nextval,例如:nextid =select childsequence.nextval from dual - 然后做你的INSERT INTO child 并指定id nextid

【讨论】:

【参考方案2】:

因为找不到外键,所以抛出异常,需要先插入依赖数据。

例子:

// Execute below query first
INSERT INTO root_table VALUES (value1, value2)
// Execute below query after root_table
INSERT INTO sub_Table VALUES (value1, value2, FK_root_ID)

【讨论】:

以上是关于在集群模式下添加子表的行时 H2 参照完整性违规的主要内容,如果未能解决你的问题,请参考以下文章

H2 参照完整性约束违反

旺旺老师笔记

Hibernate/H2 @OneToMany 删除子时“违反参照完整性约束”?

org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException:违反参照完整性约束

mysql外键(Foreign Key)的使用

混合模式下使用 H2 数据库的 Jackrabbit 集群