据我所知,postgres 中的外键约束违规不应该发生。 (带休眠)

Posted

技术标签:

【中文标题】据我所知,postgres 中的外键约束违规不应该发生。 (带休眠)【英文标题】:A foreign key constraint violation in postgres that shouldn't be happening, as far as I can tell. (w/ hibernate) 【发布时间】:2011-03-30 21:48:10 【问题描述】:

所以我有两张桌子:

table A
-id
-other stuff

table B
-id
-stuff
-a_id, a fk column to id in A

在休眠中,我将 B.a_id 映射为一个简单的属性(我不想要多对一并获取整个 A 实例,我只想要 Id)。所以假设我在 A 中有一行 id=100。

如果我尝试使用 a_id=100 在 B 中插入新行,我会收到 postgres 外键约束违规,说不存在 id=100 的 A!

我不明白这一点。 我在hibernate中打开了show_sql,它为B插入生成了这个:

insert into B (stuff, a_id) values (?, ?)

这样看起来是合法的。

我对 B.a_id 的休眠映射如下所示:

   <property name="aId" type="java.lang.Long" unique="true" not-null="true">
        <column name="a_id" />
    </property>

在 postgres 中添加的约束看起来像:

alter table B 
add constraint myfk
foreign key (a_id) 
references A;

有什么想法吗? 谢谢

edit:我认为hibernate与此无关。如果我尝试使用 sql 手动插入,我会得到同样的错误。

edit2:有一个微妙的变化 - id 字段是 int8 的,并且上面有序列:

    create table A (
id int8 not null unique,
stuff varchar(10),
primary key(id)
);
create table B (
id int8 not null unique,
a_id int8 not null references A,
primary key(id)
);

create sequence a_seq;
ALTER SEQUENCE a_seq OWNED BY a.id;
ALTER TABLE a ALTER COLUMN id SET DEFAULT nextval('a_seq');

create sequence b_seq;
ALTER SEQUENCE b_seq OWNED BY b.id;
ALTER TABLE b ALTER COLUMN id SET DEFAULT nextval('b_seq');

【问题讨论】:

SELECT * FROM a WHERE id = 100 返回什么? idPRIMARY KEYA 中吗? 【参考方案1】:
CREATE TABLE A (id INT, other_stuff VARCHAR(20) NULL);
CREATE TABLE b (id INT, stuff VARCHAR(20) NULL, a_id INT);
ALTER TABLE A ADD CONSTRAINT PK_A PRIMARY KEY (id);
ALTER TABLE B ADD CONSTRAINT myfk FOREIGN KEY (a_id) REFERENCES A;

INSERT INTO A (id) VALUES (100);
INSERT INTO B (id, a_id) VALUES (1,100);

SELECT * FROM a JOIN b ON a.id = b.a_id;
 id  | other_stuff | id | stuff | a_id 
-----+-------------+----+-------+------
 100 |             |  1 |       |  100

我尝试在 PostgreSQL 9.0.3 中使用上述 SQL 复制您的问题,但无法复制您的错误。您可以捕获的任何 DDL/DML 以及确切的错误消息都会有所帮助。

(我会在您提供更多信息时继续添加信息。希望这也可以作为其他人的起点。)

【讨论】:

谢谢。还有一点我认为不相关,但可能是相关的。见我上面的edit2 - id 字段上有序列。我开始认为这是我的 postgresql 版本中的一个微妙错误 我会冒昧地猜测像 PostgreSQL 这样成熟的产品中没有错误。我重新创建了您的表,并且无法在我的结尾处使用插入语句复制错误。你能提供正在进行的 DML 吗?【参考方案2】:

如果表 A 中的父行存在,那么 PostgreSQL 不会抛出错误。

ID 列的生成也不重要(一旦插入父行)。

我能想到的唯一一件事是:您是否可能在不同的休眠会话中将表 A 中的行插入并忘记提交?

在表 B 中插入行的另一个会话/事务将看不到表 A 中未提交的行。

直接运行语句没有问题:

postgres=> 创建表 A ( postgres(> id int8 not null unique, postgres(> 东西 varchar(10), postgres(>主键(id) postgres(>); 创建表 postgres=> postgres=> 创建表 B ( postgres(> id int8 not null unique, postgres(> a_id int8 not null 引用 A, postgres(> 东西 varchar(10), postgres(>主键(id) postgres(>); 创建表 postgres=> postgres=> 创建序列 a_seq; 创建序列 postgres=> ALTER SEQUENCE a_seq 由 a.id 拥有; 改变顺序 postgres=> ALTER TABLE a ALTER COLUMN id SET DEFAULT nextval('a_seq'); 更改表 postgres=> postgres=> 创建序列 b_seq; 创建序列 postgres=> ALTER SEQUENCE b_seq 由 b.id 拥有; 改变顺序 postgres=> ALTER TABLE b ALTER COLUMN id SET DEFAULT nextval('b_seq'); 更改表 postgres=> postgres=> 提交; 犯罪 postgres=> INSERT INTO a (stuff) VALUES ('a_stuff'); 插入 0 1 postgres=> 提交; 犯罪 postgres=> 从 a 中选择 *; 编号 |东西 --+--------- 1 | a_stuff (1 行) postgres=> INSERT INTO b (a_id, stuff) VALUES (1, 'b_stuff'); 插入 0 1 postgres=> 提交; 犯罪; postgres=>

【讨论】:

以上是关于据我所知,postgres 中的外键约束违规不应该发生。 (带休眠)的主要内容,如果未能解决你的问题,请参考以下文章

Django - 夹具中的外键

Laravel-7 迁移中的外键约束形成错误

如果我的外键已经是唯一约束的一部分,我应该索引它们吗?

使用 django-import-export 在 django 迁移中的外键

SQLSTATE [HY000] Laravel 8 中的外键约束格式错误

具有意外相似重复的 postgres 用户表导致另一个表具有混乱的外键,如何修复和合并外键?