使用事务进行Upsert

Mar*_*ri9 0 sql database postgresql spring

我正在使用Spring和PostgreSQL,我尝试使用如下代码进行一种UPSERT:

jt.update("delete from A where id = 1")
jt.update("insert into A (id, value) values (1, 100)")
Run Code Online (Sandbox Code Playgroud)

包含在事务中(使用@Transactional).

问题是,当有很多并发请求时,此代码会因"重复键"错误而失败,这意味着事务不是孤立的,或者......

我错过了有关交易如何运作的信息吗?我应该在这里使用不同的机制(例如线程同步)吗?

小智 10

我写了一篇关于它的相当大的博文,所以尽管我可能会获得链接的支持,但请阅读此内容.

要点是交易在这里没有帮助(至少在默认情况下),虽然可以写出正确的upsert,但实际上它非常棘手.


Erw*_*ter 5

假设这个简单的表:

CREATE TABLE tbl(id int primary key, value int);
Run Code Online (Sandbox Code Playgroud)

对于并发事务,此功能几乎100% 安全(见评论)。:

CREATE OR REPLACE FUNCTION f_upsert(_id int, _value int)
  RETURNS void AS
$func$
BEGIN
LOOP
   UPDATE tbl SET value = _value WHERE  id = _id;

   EXIT WHEN FOUND;

   BEGIN
      INSERT INTO tbl (id, value)
      VALUES (_id, _value);

      RETURN;

   EXCEPTION WHEN UNIQUE_VIOLATION THEN     -- tbl.id has UNIQUE constraint.
      RAISE NOTICE 'It actually happened!'; -- hardly ever happens
   END;

END LOOP;
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT f_upsert(2, 2);
Run Code Online (Sandbox Code Playgroud)

它与这个INSERT / SELECT案例非常相似,有更多的解释和链接:

  • 这与来自 http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING btw 的方法相同 (2认同)