Java 使用MySql中的记录进行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

使用JOOQ进行批量更新的正确方法是什么

我有以下资料:

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列表的大小,
    SELECT
    query可能会非常昂贵)。但是,如果没有更新的
    子句,第一种方法可能会有更高的死锁风险
  • 第一种方法可能会运行比必要时更多的update语句,例如,对于同时被删除的
    ID
    值。第二种方法可以防止这种情况发生
现在,我对MySQL还不太了解,不知道在这里批量更新语句是否更好,例如,以下类型的语句:

更新团队得分
设置分数=案例id
时间:id1然后:分数1
时间:id2然后:得分2
时间:id3然后:得分3
...
结束
其中id位于(:id1,:id2,:id3,…)

也许您可以对该方法进行基准测试,并将其与批处理进行比较(或将其与批处理相结合,例如批量更新10行并批处理所有这些批量更新)

Current;我发现第一个有效,第二个无效。我的意思是,第一个实际更新了行,但第二个没有,因为在这之后执行select会生成与更新之前相同的行。请注意:为什么在第二个示例中使用
peek()
,而不是
map()
?另外,您的映射有多大?@LukasEder
TableRecord::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();
    });
}