2016-05-22 5 views
3

Ich habe eine Transaktion, wo wir eine Zeile der Tabelle foo und dann eine Zeile der Tabelle bar einfügen. Dies stellt sicher, dass wir entweder beide oder beide Zeilen schreiben. Das Problem dabei ist bar hat einen Fremdschlüssel in foo. Da wir die id von foo zum Zeitpunkt der bar einfügen nicht kennen, scheitert dies die Fremdschlüsseleinschränkung.Wie können zwei Zeilen atomar geschrieben werden, wenn eine Zeile einen Fremdschlüssel in den anderen hat?

Früher habe ich verwendet Tools wie SQLAlchemy, wenn Python-Backends zu schreiben, dass die Fähigkeit der Spülung eine Sitzung schließen, bevor die Transaktion festgeschrieben wird - dies ermöglicht es dem Benutzer die id von foo abzuleiten und sie zusammen mit dem INSERT passieren in bar, bevor Sie tatsächlich etwas schreiben.

Meine Frage ist, im Kontext von JDBC und seiner Clojure Wrapper, wie kann dies getan werden?

Antwort

2

Zuvor versuchte ich innerhalb der Transaktion (jdbc/query db-spec ["select id from foo where name='my_foo'"]) zu verwenden, um die abhängige Zeilennummer foo abzuleiten. Dies war nil und so schien es, als ob die offensichtliche Methode nicht funktionierte. Es stellte sich jedoch heraus, dass ich db-spec und nicht die Transaktionsverbindung verwendete, die, wenn Sie jdbc/with-db-transaction verwenden, im Vektor gebunden ist.

Zum Beispiel:

(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 Die in der obigen Form wird die richtige Zeile ID ergeben.

1

Sie können die Werte in beiden Tabellen in einer Abfrage einfügen, z.B .:

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; 
1

Dies ist Teil dessen, was für DEFERRABLE foreign key constraints sind.

ALTER TABLE mytable 
    DROP CONSTRAINT the_fk_name; 

ALTER TABLE 
    ADD CONSTRAINT the_fk_name 
     FOREIGN KEY (thecol) REFERENCES othertable(othercol) 
     DEFERRABLE INITIALLY IMMEDIATE; 

dann

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; 

Ich schlage vor, mit initially immediate und set constraints, so dass der Rest der Zeit, die Sie schaffen keine Warteschlange Trigger. Es ist besser für die Leistung und Speicherverbrauch, und es wird nicht verwirren Anwendungen, die nicht verstehen und verzögerte constrasts erwarten.

Wenn Ihr Framework dies nicht bewältigen kann, können Sie statt dessen DEFERRABLE INITIALLY DEFERRED verwenden.

Verwandte Themen