我正在使用Spring和PostgreSQL,我尝试使用这样的代码做一种UPSERT:
jt.update("delete from A where id = 1")
jt.update("insert into A (id, value) values (1, 100)")
包装在一个事务中(使用@Transactional
)。
问题在于,当有很多并发请求时,此代码会因“重复密钥”错误而失败,这意味着事务不会被隔离,或者......
我错过了有关交易如何运作的信息吗?我应该在这里使用不同的机制(例如线程同步)吗?
答案 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
案例非常类似,有更多解释和链接: