我的数据库中有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。
可以通过以下两种方式之一从数据库条目计算当前聚合版本:
版本比较和插入应以原子方式完成,以防止同时插入具有相同AGGREGATE_ID和AGGREGATE_VERSION的两个事件。
很高兴:如果由于比较错误而导致插入失败,那么抛出一个不是通用SQLExeption
的异常会很好(为了以特殊的方式处理版本违规)
答案 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_increment
和identity
。您可以在documentation中了解serial
。
如文档中所述,在某些情况下可能存在差距。但是,这通常是将增加的序列号放入表中的最佳方法 - 数据库完成大部分工作。