当一行有一个外键到另一行时,如何以原子方式写入两行?

时间:2016-05-22 23:31:18

标签: postgresql jdbc clojure

我有一个事务,我们插入一行表foo,然后是一行表bar。这可以确保我们既可以写两行也可以不写。问题是bar有一个外键到foo。因为我们在id插入时不知道foo bar,所以这会使外键约束失败。

以前我在编写Python后端时使用了像SQLAlchemy这样的工具,包括在提交事务之前刷新会话的功能 - 这允许用户派生id {{1}在实际写任何内容之前,将它传递给fooINSERT

我的问题是,在JDBC及其Clojure包装器的上下文中,如何做到这一点?

3 个答案:

答案 0 :(得分:2)

以前我试图在事务中使用(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。

答案 1 :(得分:1)

您可以在一个查询中将值插入到两个表中,例如:

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;

答案 2 :(得分:1)

这是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 immediateset constraints,以便其余时间不创建排队触发器。它更适合性能和内存使用,而且它不会混淆那些不理解并期望延迟的cosntraints的应用程序。

如果您的框架无法解决此问题,您可以使用DEFERRABLE INITIALLY DEFERRED代替。