是否无需使用for循环即可实现SQL批量更新List中的多个元素
优化可行性结论
完全可以将SQL调用移出循环,这也是非常典型的ORM性能优化手段,能够避免循环单条更新带来的N次数据库IO开销,优化后性能会有明显提升,且可以保证功能完全一致。
先说明现有代码的疑似逻辑问题
你贴出的代码存在明显的语法问题:duplicateElement是遍历用的Foo集合,不能直接调用getName()方法。结合你的业务场景,大概率是两种逻辑:
- 要遍历修改每个Foo对象自身的name字段,再更新该对象
- 有一个单独的公共
duplicateElement参数对象,修改这个对象的字段后,把所有遍历到的Foo都更新为该对象的值
以下针对两种场景分别给出优化方案:
场景1:每个Foo修改自身的name字段后更新
原逻辑对齐修正
// 修正后符合语法的原逻辑 for (Foo foo: duplicateElementList) { String newName = foo.getName() .replace(split + name + split, split); foo.setName(newName); foo.updateFoo(foo); // 每次循环触发一次单条更新SQL }
优化方案(SQL移出循环)
先在内存中完成所有对象的字段修改,再调用一次批量更新接口完成数据库操作:
- 内存处理所有待更新对象
List<Foo> toUpdateList = new ArrayList<>(duplicateElementList.size()); for (Foo foo: duplicateElementList) { String newName = foo.getName() .replace(split + name + split, split); foo.setName(newName); toUpdateList.add(foo); }
- 执行批量更新,仅触发一次SQL交互
不同持久层框架的批量更新实现可以选以下任意一种适配:
- MyBatis:写foreach拼接多更新语句,需要JDBC连接参数添加
allowMultiQueries=true开启多语句执行
<update id="batchUpdateFooName"> <foreach collection="list" item="item" separator=";"> UPDATE foo_table SET name = #{item.name} WHERE id = #{item.id} </foreach> </update>
- JPA/Hibernate:直接调用
saveAll(toUpdateList)方法,配置好批量更新参数即可 - JDBC:使用
PreparedStatement的addBatch()+executeBatch()实现批量提交
更优方案:直接数据库层面批量更新
如果不需要在内存中获取修改后的name值,完全可以直接用一条SQL完成所有更新,连内存遍历都不需要,性能最高:
UPDATE foo_table SET name = REPLACE(name, CONCAT(#{split}, #{targetName}, #{split}), #{split}) WHERE id IN (<foreach collection="idList" item="id" separator=",">#{id}</foreach>)
场景2:所有Foo统一更新为公共duplicateElement的值
如果你的业务是修改公共的duplicateElement对象后,把所有遍历的Foo都更新为该对象的值,优化更简单:
- 先在循环外完成duplicateElement的name字段修改
- 直接调用批量更新接口,按id列表更新所有对应记录的字段为duplicateElement的值即可,不需要循环处理
// 循环外先完成字段修改 String newName = duplicateElement.getName() .replace(split + name + split, split); duplicateElement.setName(newName); // 提取所有待更新的id List<Long> idList = duplicateElementList.stream().map(Foo::getId).collect(Collectors.toList()); // 调用批量更新方法,一次SQL完成 fooMapper.batchUpdateByIdList(idList, duplicateElement);
功能一致性验证
只要批量更新的字段、更新条件和原循环单条更新的逻辑完全对齐,最终的数据库操作结果没有任何差异,不会影响原有功能。
内容的提问来源于stack exchange,提问作者Thanh Hiếu Nguyễn
相关产品推荐
相关产品推荐

