【问题标题】:How can two rows be written atomically when one row has a foreign key into the other?当一行有一个外键到另一行时,如何原子地写入两行?
【发布时间】:2016-05-22 23:31:18
【问题描述】:
我有一个事务,我们插入一行表foo,然后插入一行表bar。这确保我们要么写两行,要么都不写。问题在于bar 有一个指向foo 的外键。因为在插入bar 时我们不知道foo 的id,所以外键约束失败。
以前我在编写 Python 后端时使用过 SQLAlchemy 之类的工具,其中包括在提交事务之前刷新会话的能力——这允许用户派生 foo 的 id 并将其传递到INSERT 到bar 在实际写任何东西之前。
我的问题是,在 JDBC 及其 Clojure 包装器的上下文中,如何做到这一点?
【问题讨论】:
标签:
postgresql
jdbc
clojure
【解决方案1】:
之前我试图在事务中使用(jdbc/query db-spec ["select id from foo where name='my_foo'"]) 来派生相关的foo 行ID。这正在返回nil,所以看起来明显的方法不起作用。然而事实证明我使用的是db-spec,而不是事务连接,如果你使用jdbc/with-db-transaction,它会绑定在向量中。
例如:
(jdbc/with-db-transaction [t-conn db-spec]
(jdbc/insert! t-conn :foo {:name "my_foo"})
(jdbc/query t-conn ["select id from foo where name='my_foo'"]))
上述表格中的query 将产生正确的行ID。
【解决方案2】:
您可以在一个查询中将值插入两个表中,例如:
create table foo (
foo_id serial primary key,
name text);
create table bar (
bar_id serial primary key,
foo_id int references foo,
name text);
with insert_into_foo as (
insert into foo (name)
values ('some foo')
returning foo_id
)
insert into bar (foo_id, name)
select foo_id, 'some bar'
from insert_into_foo;
【解决方案3】:
这是DEFERRABLE foreign key constraints 的一部分。
ALTER TABLE mytable
DROP CONSTRAINT the_fk_name;
ALTER TABLE
ADD CONSTRAINT the_fk_name
FOREIGN KEY (thecol) REFERENCES othertable(othercol)
DEFERRABLE INITIALLY IMMEDIATE;
然后
BEGIN;
SET CONSTRAINTS DEFERRED;
INSERT thetable ...;
INSERT INTO othertable ...;
-- optional, but if you do this you get any errors BEFORE commit
SET CONSTRAINTS IMMEDIATE;
COMMIT;
我建议使用initially immediate 和set constraints,以便在其余时间不要创建排队触发器。它对性能和内存使用更好,而且不会混淆不理解和期望延迟 cosntrains 的应用程序。
如果您的框架无法解决这个问题,您可以改用DEFERRABLE INITIALLY DEFERRED。