使用事务进行Upsert

时间:2013-06-17 16:57:25

标签: sql database spring postgresql

我正在使用Spring和PostgreSQL,我尝试使用这样的代码做一种UPSERT:

jt.update("delete from A where id = 1")
jt.update("insert into A (id, value) values (1, 100)")

包装在一个事务中(使用@Transactional)。

问题在于,当有很多并发请求时,此代码会因“重复密钥”错误而失败,这意味着事务不会被隔离,或者......

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

2 个答案:

答案 0 :(得分:10)

我写了一篇相当大的博客文章,所以尽管我可能会对链接进行投票,但请阅读this

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

答案 1 :(得分:3)

假设这个简单的表:

CREATE TABLE tbl(id int primary key, value int);

对于并发事务,此函数几乎 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;

呼叫:

SELECT f_upsert(2, 2);

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