Jooq 中的原子批量插入/更新
Atomic Batch Insert / Update in Jooq
我有一个程序可以监视服务并保存它们的当前状态。我的程序定期检查每个服务并将有关它们的信息存储在 postgres 中。大多数时候,这涉及更新数据库中的现有行。但有时也需要添加新服务。
我将它们存储在一个看起来像这样的模式中:
CREATE TABLE services IF NOT EXISTS (
id SERIAL NOT NULL,
name TEXT NOT NULL,
status TEXT ,
PRIMARY KEY (id, name)
)
其中 ID 指定一台机器,名称指定该机器上的一项服务。 POJO 非常简单:
public class Service {
int id;
String name;
String status;
}
例如,我的 table 可能只有一行开始:(22, "api", "active")
。在预定的时间间隔内,我的程序确定一台机器上现在有 2 个服务 运行ning,并且当前服务的状态已更改:
- (22, "api", "错误")
- (22, "消息", "活跃")
我的方法是这样的:
Set<ServicesRecord> records = listServices
.stream()
.map(service -> {
ServicesRecord record = new ServicesRecord();
record.setId(service.id);
record.setName(service.name);
record.setStatus(service.status);
return record;
})
.collect(Collectors.toSet());
DSLContext dsl = DSL.using(this.configuration);
dsl.batchStore(records).execute();
}
但是,当我尝试 运行 它时,这给了我一个错误:
Caused by: org.jooq.exception.DataAccessException: SQL [insert into "foobar"."services" ("id", "name", "status", values (?, ?, ?)]; Batch entry 0 insert into "foobar"."services" ("id", "name", "status") values (1, 'testName', 'baz') was aborted: ERROR: duplicate key value violates unique constraint "services_pkey"
我可以看到 batchStore
失败,因为它使用了 UpdatableRecord
的 store
方法,而后者又失败了,因为我正在创建一个新记录,而不是从 jooq 获取记录。
我正在考虑这样的替代方法:
- 从数据库中获取所有记录。
- 对于匹配
records
的每条记录,我将 changed
设置为 true
。
- 那我运行
batchStore
或者,我可以这样做吗?
- 从数据库中获取所有记录。
- 创建要插入的记录列表并使用
batchInsert
- 制作要更新的记录列表,修改他们的记录,然后使用
batchStore
但是这两个 运行 都存在非原子化的风险。如果在执行第 2 步时数据库发生变化,我对 batchStore
的使用仍然会失败。我想要的是一种在单个操作中进行这种批量存储的方法,而不是先获取再执行。有办法吗?
您要找的是batchMerge()
, which will ship with jOOQ 3.14: https://github.com/jOOQ/jOOQ/issues/10046
或者,您可以使用 data import API. You can then combine the onDuplicateKeyUpdate()
clause with appropriate batch sizes。
或者,您可以显式写 INSERT .. ON CONFLICT
statements and batch them explicitly。
我有一个程序可以监视服务并保存它们的当前状态。我的程序定期检查每个服务并将有关它们的信息存储在 postgres 中。大多数时候,这涉及更新数据库中的现有行。但有时也需要添加新服务。
我将它们存储在一个看起来像这样的模式中:
CREATE TABLE services IF NOT EXISTS (
id SERIAL NOT NULL,
name TEXT NOT NULL,
status TEXT ,
PRIMARY KEY (id, name)
)
其中 ID 指定一台机器,名称指定该机器上的一项服务。 POJO 非常简单:
public class Service {
int id;
String name;
String status;
}
例如,我的 table 可能只有一行开始:(22, "api", "active")
。在预定的时间间隔内,我的程序确定一台机器上现在有 2 个服务 运行ning,并且当前服务的状态已更改:
- (22, "api", "错误")
- (22, "消息", "活跃")
我的方法是这样的:
Set<ServicesRecord> records = listServices
.stream()
.map(service -> {
ServicesRecord record = new ServicesRecord();
record.setId(service.id);
record.setName(service.name);
record.setStatus(service.status);
return record;
})
.collect(Collectors.toSet());
DSLContext dsl = DSL.using(this.configuration);
dsl.batchStore(records).execute();
}
但是,当我尝试 运行 它时,这给了我一个错误:
Caused by: org.jooq.exception.DataAccessException: SQL [insert into "foobar"."services" ("id", "name", "status", values (?, ?, ?)]; Batch entry 0 insert into "foobar"."services" ("id", "name", "status") values (1, 'testName', 'baz') was aborted: ERROR: duplicate key value violates unique constraint "services_pkey"
我可以看到 batchStore
失败,因为它使用了 UpdatableRecord
的 store
方法,而后者又失败了,因为我正在创建一个新记录,而不是从 jooq 获取记录。
我正在考虑这样的替代方法:
- 从数据库中获取所有记录。
- 对于匹配
records
的每条记录,我将changed
设置为true
。 - 那我运行
batchStore
或者,我可以这样做吗?
- 从数据库中获取所有记录。
- 创建要插入的记录列表并使用
batchInsert
- 制作要更新的记录列表,修改他们的记录,然后使用
batchStore
但是这两个 运行 都存在非原子化的风险。如果在执行第 2 步时数据库发生变化,我对 batchStore
的使用仍然会失败。我想要的是一种在单个操作中进行这种批量存储的方法,而不是先获取再执行。有办法吗?
您要找的是batchMerge()
, which will ship with jOOQ 3.14: https://github.com/jOOQ/jOOQ/issues/10046
或者,您可以使用 data import API. You can then combine the onDuplicateKeyUpdate()
clause with appropriate batch sizes。
或者,您可以显式写 INSERT .. ON CONFLICT
statements and batch them explicitly。