SQL条件原子插入

时间:2017-07-02 11:49:13

标签: java sql postgresql jdbc

我的数据库中有EVENTS表。定期插入新事件将如下所示:

private static final String INSERT_EVENT_SQL = "INSERT INTO EVENTS"
    + "(EVENT_ID, AGGREGATE_ID, AGGREGATE_VERSION, EVENT_TYPE, EVENT_PAYLOAD) VALUES"
    + "(?,?,?,?,?)";

pst = conn.prepareStatement(INSERT_EVENT_SQL);
pst.setString(1, event.getEventId().toString());
pst.setString(2, event.getAggregateId().toString());
pst.setLong(3, event.getAggregateVersion());
pst.setString(4, event.getEventType());
pst.setString(5, event.getPayload());

我想使这个插入条件和原子。

必须满足的条件是event.getAggregateVersion()等于数据库中存储的当前聚合版本加1。

可以通过以下两种方式之一从数据库条目计算当前聚合版本:

  1. 查找具有相同AGGREGATE_ID的最新事件并获取其AGGREGATE_VERSION
  2. 具有相同AGGREGATE_ID的所有事件中AGGREGATE_VERSION的最大值
  3. 版本比较和插入应以原子方式完成,以防止同时插入具有相同AGGREGATE_ID和AGGREGATE_VERSION的两个事件。

    很高兴:如果由于比较错误而导致插入失败,那么抛出一个不是通用SQLExeption的异常会很好(为了以特殊的方式处理版本违规)

2 个答案:

答案 0 :(得分:1)

你可以这样写:

private static final String INSERT_EVENT_SQL = ""
    + "WITH data (event_id, aggregate_id, aggregate_version, event_type, event_payload) AS"
    + "("
    + "    VALUES(?, ?, ?, ?, ?, ?)"
    + ")"
    + "INSERT INTO "
    + "   events"
    + "   (event_id, aggregate_id, aggregate_version, event_type, event_payload)"
    + "SELECT"
    + "   *"
    + "FROM"
    + "   data"
    + "WHERE"
    + "   data.aggregate_version = "
    + "      coalesce ((SELECT max(events.aggregate_version)"
    + "                 FROM events "
    + "                 WHERE events. aggregate_id = data. aggregate_id"
    + "                ), 0) + 1"
    + "RETURNING"
    + "   event_id, aggregate_id ;"

    // bind parameters
    // execute
    // get results

检查是否有任何行返回;并采取相应行动。你应该将它包装成TRANSACTION并发布SET TRANSACTION ISOLATION LEVEL SERIALIZABLE,如 Craig Ringer 所述,以避免并发风险。即使这只是一个语句,另一个并行工作的进程(或外部客户端)在计算INSERT时也可能max行。

我没有完全掌握你的专栏的含义,也不知道你为什么要做你正在做的事情。所以,我已经做了一些我自己的假设,在寻找max时可能做正确的事。您可能应该更改WHERE

请参阅 dbfiddle here ,了解其运行方式。

答案 1 :(得分:0)

在Postgres中增加列的常规方法是将其声明为SERIAL

create table . . . (
    tableId serial primary key,
    . . .
);

这在功能上等同于其他数据库中的auto_incrementidentity。您可以在documentation中了解serial

如文档中所述,在某些情况下可能存在差距。但是,这通常是将增加的序列号放入表中的最佳方法 - 数据库完成大部分工作。