如何使用jooq批量更新

ppp*_*van 2 java jooq

使用以下方式使用 jOOQ 进行更新。

for (Balance balance : balances) {
       dslContext.update(BALANCE)
                 .set(BALANCE.AMOUNT, balance.getAmount())
                 .where(BALANCE.ID.eq(balance.getId))
                 .execute();
}
Run Code Online (Sandbox Code Playgroud)

这将遍历所有余额并插入每个余额。我知道这可以使用 UpdatableRecord 来完成。但我想避免从数据库中获取余额。Balance 是包含超过 8 个字段的表,但我只对更新一个字段感兴趣。如果没有 UpdatableRecord,还有其他方法可以做到这一点吗?

Sim*_*lli 5

Your code is fine but you could use the batch updates like this:

List<UpdateConditionStep<BalanceRecord> updates = new ArrayList<>();
for (Balance balance : balances) {
   updates.add(dslContext.update(BALANCE)
             .set(BALANCE.AMOUNT, balance.getAmount())
             .where(BALANCE.ID.eq(balance.getId)));
}

dslContext.batch(updates).execute();
Run Code Online (Sandbox Code Playgroud)

Documentation: https://www.jooq.org/doc/3.14/manual-single-page/#batch-execution

Just a hint: Be careful of lost updates if you update a balance without locking.


Luk*_*der 5

使用批处理语句 UpdatableRecord

UpdatableRecord如果您想要方便,您仍然可以使用DSLContext.batchUpdate(),而无需先从数据库中获取所有记录。假设您正在使用代码生成器并且正在生成记录,您将拥有BalanceRecord

ctx.batchUpdate(balances
   .stream()
   .map(b -> { 
       var r = new BalanceRecord();
       r.setAmount(b.getAmount());
       r.setId(b.getId());
       r.changed(BALANCE.ID, false); // Prevent setting the ID to itself
       return r;
   })
   .collect(toList()))
   .execute();
Run Code Online (Sandbox Code Playgroud)

这将为您在幕后创建一个批处理语句

用一个 BatchedConnection

从 jOOQ 3.14 开始,您可以使用 a 透明地批处理所有逻辑BatchedConnection,这是一个特殊的 JDBC 连接代理,它延迟所有 JDBC 语句(jOOQ 创建或未创建)的执行,缓冲它们直到需要执行:

dslContext.batched(c -> {
    for (Balance balance : balances) {
        c.dsl().update(BALANCE)
               .set(BALANCE.AMOUNT, balance.getAmount())
               .where(BALANCE.ID.eq(balance.getId))
               .execute(); // This doesn't execute the query yet
    }
} // Now, the buffered queries are being batch-executed
Run Code Online (Sandbox Code Playgroud)

运行单个批量语句

根据您的评论,似乎希望将其作为单个批量语句运行,而不是在单个批处理语句中批处理多个语句(单次往返)。

我不相信这是正确的方法 - 语句可能会变得很大并且不一定比批处理快,但是当然,您可以使用表达式来做到这一点CASE

ctx.update(BALANCE)
   .set(BALANCE.AMOUNT, 
     case_(BALANCE.ID).mapValues(
       balances.stream().collect(toMap(balance::getId, balance::getValue))
     )
   )
   .where(BALANCE.ID.in(balances.stream().map(balance::getId).collect(toList())))
   .execute();
Run Code Online (Sandbox Code Playgroud)

这将产生类似的东西:

UPDATE balance
SET amount = 
  CASE id
    WHEN 1 THEN 2.50
    WHEN 2 THEN 3.50
    WHEN 13 THEN 8.30
  END
WHERE id IN (1, 2, 13)
Run Code Online (Sandbox Code Playgroud)

根据您使用的方言,这可能会更好地完成MERGE

MERGE INTO balance
USING (
  VALUES (1, 2.50), (2, 3.50), (13, 8.30)
) AS s (i, b)
ON balance.id = s.i 
AND balance.balance = s.b
WHEN MATCHED THEN UPDATE SET balance = b
Run Code Online (Sandbox Code Playgroud)

  • `DSLContext.batchMerge()` 仅在 jOOQ 3.14 中添加(目前尚未发布)。您只需将“batchUpdate()”调用更改为“batchMerge()” (2认同)