管理插入更新计划任务

时间:2017-07-16 15:21:01

标签: spring postgresql scheduled-tasks

我不习惯使用预定的任务,我需要一些建议(我认为好或坏

我正在设计一个每20分钟运行一次的功能。此函数从json文件(我无法控制)中检索数据,并将数据插入数据库。

当我这样做时,我不认为这会在数据库视图中创建一个唯一的ID问题,即它是每次更新的相同数据。

我想过做两个功能:

1:第一次插入(INSERT)

2:根据ID(更新)

更新数据
@Component
public class LoadSportsCompetition {

  @PostConstruct
  public void insert() {
    // 1 : get json data
    // 2 : insert in DB
  }

  @Scheduled(cron="0 0/20 * * * ?")
  public void update() {
    // 1 : get json data
    // 2 : update rows by ID      
  }

}

1 个答案:

答案 0 :(得分:1)

在PostgreSQL 9.5及更高版本中处理此问题的(最可能)最佳方法是使用INSERT ... ON CONFLICT ... DO UPDATE

让我们假设这是你的原始表(非常简单,为了这个例子):

CREATE TABLE tbl
(
    tbl_id INTEGER,
    payload JSONB,

    CONSTRAINT tbl_pk 
       PRIMARY KEY (tbl_id)
) ;

我们用起始数据填充它:

INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (1, '{"a":12}'),
   (2, '{"a":13, "b": 25}'),
   (3, '{"a":15, "b": [12,13,14]}'),
   (4, '{"a":12, "c": "something"}'),
   (5, '{"a":13, "x": 1234.567}'),
   (6, '{"a":12, "x": 1234.789}') ;

现在我们执行一个非冲突的插入(即:ON CONFLICT ...... DO将不会被执行):

-- A normal insert, no conflict
INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (7, '{"x": 1234.56, "y": 3456.78}')
ON CONFLICT ON CONSTRAINT tbl_pk DO 
UPDATE
   SET payload = excluded.payload ;   -- Note: the excluded pseudo-table comprises the conflicting rows

现在我们执行一个INSERT会产生PRIMARY KEY冲突,该冲突将由ON CONFLICT子句处理并执行更新

-- A conflicting insert
INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (3, '{"a": 16, "b": "I don''t know"}')
ON CONFLICT ON CONSTRAINT tbl_pk DO 
UPDATE
   SET payload = excluded.payload ;  

现在,两行插入将在一行上发生冲突,并插入另一行:

-- Now one of each
-- A conflicting insert
INSERT INTO tbl
   (tbl_id, payload)
VALUES
   (4, '{"a": 18, "b": "I will we updated"}'),
   (9, '{"a": 17, "b": "I am nuber 9"}')
ON CONFLICT ON CONSTRAINT tbl_pk DO UPDATE
   SET payload = excluded.payload ; 

我们现在检查表:

SELECT * FROM tbl ORDER BY tbl_id ;
tbl_id | payload                            
-----: | :----------------------------------
     1 | {"a": 12}                          
     2 | {"a": 13, "b": 25}                 
     3 | {"a": 16, "b": "I don't know"}     
     4 | {"a": 18, "b": "I will we updated"}
     5 | {"a": 13, "x": 1234.567}           
     6 | {"a": 12, "x": 1234.789}           
     7 | {"x": 1234.56, "y": 3456.78}       
     9 | {"a": 17, "b": "I am nuber 9"}     

您的代码应遍历您的传入数据,获取它,并一次执行所有INSERT/UPDATE(有时称为MERGEUPSERT)一行,或分批执行多行VALUES

您可以在 dbfiddle here

获取所有代码

还有一种替代方案,如果您分批工作,则更适合。使用WITH语句,该语句包含一个UPDATE子句,后跟INSERT个句子:

-- Avoiding (most) concurrency issues.

BEGIN TRANSACTION ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ;

WITH data_to_load (tbl_id,  payload) AS
(
    VALUES
       (3, '{"a": 16, "b": "I don''t know"}' :: jsonb),
       (4, '{"a": 18, "b": "I will we updated"}'),
       (7, '{"x": 1234.56, "y": 3456.78}'),
       (9, '{"a": 17, "b": "I am nuber 9"}')
), 
update_existing AS
(
    UPDATE
        tbl
    SET 
        payload = data_to_load.payload
    FROM
        data_to_load
    WHERE
        tbl.tbl_id = data_to_load.tbl_id
)
-- Insert the non-existing
INSERT INTO
    tbl
    (tbl_id, payload)
SELECT
    tbl_id, payload
FROM
    data_to_load
WHERE
    data_to_load.tbl_id NOT IN (SELECT tbl_id FROM tbl) ;

COMMIT TRANSACTION ;

您将获得相同的结果,您可以在 dbfiddle here 中看到。

在这两种情况下,都准备好进行错误处理,并准备好重试事务,如果它们因并发操作也会因修改数据库而发生冲突。您的事务可以是显式的(如第二种情况)或隐式,如果您对每个INSERT进行某种自动提交