在实际开发中,我们经常会遇到需要批量处理数据的场景,特别是需要根据某些条件判断是插入新数据还是更新已有数据的情况。本文将详细介绍基于MyBatis实现批量新增或更新的几种高效方案。
批量新增或更新(通常称为"upsert")的典型场景包括:
这类操作的核心需求是:根据某些字段判断数据是否存在,存在则更新,不存在则插入
实现方式:在Java代码中循环遍历集合,对每条数据单独执行查询,然后根据查询结果决定执行插入或更新。
javapublic void updateBatch(List<MyData> datas){
for(MyData data : datas){
try{
MyData existing = myDataDao.selectById(data.getId());
if(existing != null){
myDataDao.update(data);
}else{
myDataDao.insert(data);
}
}catch(Exception e){
// 异常处理
}
}
}
优缺点分析:
实现方式:通过MyBatis的foreach标签拼接多条update语句,需要MySQL连接参数添加allowMultiQueries=true
xml<update id="updateBatch" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
update course
<set>
name=${item.name}
</set>
where id = ${item.id}
</foreach>
</update>
优缺点分析:
实现方式:利用SQL的CASE WHEN语法实现单条SQL批量更新多条数据
xml<update id="updateBatch" parameterType="java.util.List">
update mydata_table
set status=
<foreach collection="list" item="item" index="index"
separator=" " open="case ID" close="end">
when #{item.id} then #{item.status}
</foreach>
where id in
<foreach collection="list" index="index" item="item"
separator="," open="(" close=")">
#{item.id,jdbcType=BIGINT}
</foreach>
</update>
注意事项:
xml<update id="updateChartParamByAccountAndChartid" parameterType="list">
update followme_parameters
<trim prefix="set" suffixOverrides=",">
<trim prefix="signal_source =case" suffix="end,">
<foreach collection="list" item="item" index="index">
<if test="item.signalSource!=null">
when account=#{item.account} and chart_id=#{item.chartId}
then #{item.signalSource}
</if>
</foreach>
</trim>
<trim prefix="rate =case" suffix="end,">
<foreach collection="list" item="item" index="index">
<if test="item.rate!=null">
when account=#{item.account} and chart_id=#{item.chartId}
then #{item.rate}
</if>
</foreach>
</trim>
</trim>
where id in
<foreach collection="list" item="item" index="index" separator="," open="(" close=")">
#{item.id}
</foreach>
</update>
实现方式:利用MySQL特有的ON DUPLICATE KEY UPDATE
语法实现插入或更新
xml<insert id="batchInsertOrUpdate">
INSERT INTO hh_adx_monitor_summary
(code, plan_id, cons, exp, conv, click, dimension_time)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.code}, #{item.planId}, #{item.cons}, #{item.exp},
#{item.conv}, #{item.click}, #{item.dimensionTime})
</foreach>
ON DUPLICATE KEY UPDATE
code = VALUES(code),
plan_id = VALUES(plan_id),
cons = VALUES(cons),
exp = VALUES(exp),
conv = VALUES(conv),
click = VALUES(click),
dimension_time = VALUES(dimension_time)
</insert>
前提条件:
实现方式:使用MyBatis的BATCH执行器,配合标准insert和update语句
java@Test
public void testInsertWithMapper() {
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
Mapper userMapper = sqlSession.getMapper(Mapper.class);
User user1 = new User(null, "Pocoyo");
userMapper.insert(user1);
User user2 = new User(null, "Valentina");
userMapper.insert(user2);
sqlSession.flushStatements();
sqlSession.commit();
}
}
特点:
如果你使用MyBatis-Plus,它提供了更简便的方式:
java// 服务接口继承IService
public interface IHhChainCustomerInfoService extends IService<HhChainCustomerInfo> {}
// 服务实现类继承ServiceImpl
public class HhChainCustomerInfoServiceImpl extends ServiceImpl<HhChainCustomerInfoMapper, HhChainCustomerInfo>
implements IHhChainCustomerInfoService {}
// 使用示例
List<HhChainCustomerInfo> list = ...;
hhChainCustomerInfoService.saveOrUpdateBatch(list);
注意事项:
对于开启逻辑删除的场景,可以通过自定义SQL注入器实现不带逻辑删除的批量操作
javapublic class SelectListWithoutLogicDelete extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
// 实现省略...
}
@Override
protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
// 重写去掉逻辑删除相关代码
}
}
方案 | 性能 | 复杂度 | 兼容性 | 适用场景 |
---|---|---|---|---|
逐条处理 | 差 | 低 | 高 | 数据量小,逻辑复杂 |
foreach多条SQL | 中 | 中 | 中(需数据库支持) | 中等数据量 |
CASE WHEN批量 | 优 | 高 | 中 | 大数据量,MySQL |
ON DUPLICATE KEY | 优 | 中 | 低(MySQL特有) | MySQL,大数据量 |
MyBatis批处理 | 中 | 中 | 高 | 多数据库环境 |
MyBatis-Plus | 中 | 低 | 高 | 快速开发,中小数据量 |
对于MySQL数据库,ON DUPLICATE KEY UPDATE方式通常是性能最好的选择,但需要注意它只适用于MySQL。如果需要兼容多种数据库,CASE WHEN批量更新或MyBatis批处理执行器是更好的选择。对于简单场景或中小数据量,使用MyBatis-Plus的saveOrUpdateBatch可以大大提高开发效率。
无论选择哪种方案,都应该根据实际业务场景、数据量和性能要求进行选择,并在测试环境中验证其性能和正确性。