Java 使用MySql中的记录进行Jooq批量更新
使用JOOQ进行批量更新的正确方法是什么 我有以下资料:Java 使用MySql中的记录进行Jooq批量更新,java,mysql,java-8,batch-processing,jooq,Java,Mysql,Java 8,Batch Processing,Jooq,使用JOOQ进行批量更新的正确方法是什么 我有以下资料: public void updateScores(Map<String, Integer> teamScores) { writeContext().transaction(config -> { DSLContext dslContext = DSL.using(config); dslContext.batchUpdate(Maps.transformEntries(teamS
public void updateScores(Map<String, Integer> teamScores) {
writeContext().transaction(config -> {
DSLContext dslContext = DSL.using(config);
dslContext.batchUpdate(Maps.transformEntries(teamScores, (id, score) -> {
TeamScoresRecord record = new TeamScoresRecord();
record.setTeamId(id);
record.setScore(score);
return record;
}).values()).execute();
});
}
public void updateScores(映射teamScores){
writeContext().事务(配置->{
DSLContext=DSL.using(config);
dslContext.batchUpdate(映射.transformEntries(teamScores,(id,score)->{
TeamScoresRecord记录=新的TeamScoresRecord();
记录。记录(id);
记录。设置核心(分数);
返回记录;
}).values()).execute();
});
}
或
public void updateScores(映射teamScores){
writeContext().事务(配置->{
DSLContext=DSL.using(config);
dslContext.batchUpdate(
dslContext.selectFrom(团队分数)
.where(TEAM_SCORES.TEAM_ID.in(teamScores.keySet()))
.forUpdate()
.fetch()
.stream()
.peek(record->record.setScore(teamScores.get(record.getTeamId()))
.collect(收集器.toList())
).execute();
});
}
以下哪种是批量更新的推荐方式?这个问题显然非常主观,不容易回答。以下是一些观察结果:
- 即使在jOOQ中使用批处理API,您也会运行大量的单独查询,这会带来相当大的客户机/服务器开销。在您的特定情况下,这可能是好的,但是考虑将整个逻辑移动到数据库中通常是不坏的,例如,通过在临时表中插入映射,然后使用批量<代码>更新< /COD>语句 合并这两个数据集。
- 如果两个冲突的批以不同的顺序更新相同的记录,则两个批都可能导致数据库死锁。我不确定这是否是代码中的问题,但您可能希望确保这永远不会发生
- 第一种方法将少运行一个查询(根据in列表的大小,
query可能会非常昂贵)。但是,如果没有更新的SELECT
子句,第一种方法可能会有更高的死锁风险
- 第一种方法可能会运行比必要时更多的update语句,例如,对于同时被删除的
值。第二种方法可以防止这种情况发生ID
更新团队得分
设置分数=案例id
时间:id1然后:分数1
时间:id2然后:得分2
时间:id3然后:得分3
...
结束
其中id位于(:id1,:id2,:id3,…)
也许您可以对该方法进行基准测试,并将其与批处理进行比较(或将其与批处理相结合,例如批量更新10行并批处理所有这些批量更新)Current;我发现第一个有效,第二个无效。我的意思是,第一个实际更新了行,但第二个没有,因为在这之后执行select会生成与更新之前相同的行。请注意:为什么在第二个示例中使用
peek()
,而不是map()
?另外,您的映射有多大?@LukasEderTableRecord::set
methods返回void,并在适当的位置修改记录,因此使用map可能会因为void而出错,但使用peek会起作用,因为项在适当的位置被修改。我明白了,地图通常在10k-15k左右。还有Record::with
,以防万一,或者您可以在代码生成器中生成流畅的setter。我将在以后研究这个问题。恐怕我不能用第二种方法重现这个问题。我用H2数据库测试这个问题,因为这是我们用来测试的,所以我不确定它是否也适用于mysql。您知道在任意db上测试jooq的方法吗?数据库使用InnoDB引擎,该引擎实现行级锁定,因此第二个引擎不应导致整个表被锁定。我再次尝试了第二个版本,它很有效。我以前可能用错了桌子。不管怎样,从你所说的,我会回到旧的一个,因为它似乎有一个肚子最小的潜力up@smac89:请提出有关测试数据库的新问题。
public void updateScores(Map<String, Integer> teamScores) {
writeContext().transaction(config -> {
DSLContext dslContext = DSL.using(config);
dslContext.batchUpdate(
dslContext.selectFrom(TEAM_SCORES)
.where(TEAM_SCORES.TEAM_ID.in(teamScores.keySet()))
.forUpdate()
.fetch()
.stream()
.peek(record -> record.setScore(teamScores.get(record.getTeamId())))
.collect(Collectors.toList())
).execute();
});
}