mybatis批量处理数据的方式

for循环执行sql

很慢,不推荐

foreach动态拼接sql来插入

通用的拼接方式(mysql和oracle都适用)

mysql和oracle都适用

<insert id="batchInsert" parameterType="java.util.List">
    INSERT INTO T_FUND_OPERATE_LOG 
        (CHANGE_RECORD_ID , CHANGE_RECORD_GROUP_ID)
    <foreach collection="list" item="item" separator="UNION ALL">
        SELECT #{item.changeRecordId,jdbcType=VARCHAR},
               #{item.changeRecordGroupId,jdbcType=VARCHAR}
        FROM DUAL
    </foreach>
</insert>

只适用于mysql的语法

INSERT INTO TABLE () VALUE (),()

<insert id="batchInsert">
    INSERT INTO T_SYS_GROUP (GROUP_ID, GROUP_NAME)
    VALUES
    <foreach collection="sysGroups" item="item" separator=",">
        (#{item.groupId,jdbcType=VARCHAR}, 
        #{item.groupName,jdbcType=VARCHAR})
    </foreach>
</insert>

只适用于oracle的语法

INSERT ALL INTO TABLE () VALUE () INTO TABLE () VALUE () SELECT 1 FROM DUAL

<insert id="batchInsert" parameterType="java.util.List">
    INSERT ALL
    <foreach collection="list" item="item" separator=" ">
        INTO T_I18N_DYNAMIC
        (I18N_DYNAMIC_ID, DATA_ID)
        VALUE (
        #{item.id,jdbcType=VARCHAR},
        #{item.dataId,jdbcType=VARCHAR}
        )
    </foreach>
    SELECT 1 FROM DUAL
</insert>

直接多条sql拼接(插入、更新、删除)

如果是mysql数据库,需要在Mysql连接字符串里添加allowMultiQueries=true

mysql

<update id="batchUpdate">
    <foreach collection="list" item="item" separator=";">
        UPDATE T_I18N_DYNAMIC SET
        DATA_ID = #{item.dataId, jdbcType=VARCHAR}
        where I18N_DYNAMIC_ID = #{item.id, jdbcType=VARCHAR}
    </foreach>
</update>

oracle

<update id="batchUpdate" parameterType="java.util.List">
    begin
    <foreach collection="list" item="item" separator=";">
        UPDATE T_I18N_DYNAMIC SET
        DATA_ID = #{item.dataId, jdbcType=VARCHAR}
        where I18N_DYNAMIC_ID = #{item.id, jdbcType=VARCHAR}
    </foreach>
    ;end;
</update>

mybatis批处理

mysql和oracle都适用
如果是mysql数据库,需要在mysql连接字符串里添加rewriteBatchedStatements=true

批处理工具类

import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

import java.util.Collection;
import java.util.function.BiConsumer;

/**
 * Mybatis批处理执行sql的工具类
 * <pre>
 *     参考自com.baomidou.mybatisplus.extension.toolkit.SqlHelper#executeBatch
 *     使用该工具类的前提是:
 *      如果是Mysql环境:
 *          需要在连接字符串里加入&rewriteBatchedStatements=true
 * </pre>
 *
 * @author 肖浩然
 * @version fssc1.0
 * @since 3.4.0
 */
public class SqlBatchUtils {
    /**
     * 默认批量执行的数量
     */
    public static final int BATCH_SIZE = 999;
    /**
     * sqlSessionFactory
     */
    private static final SqlSessionFactory sqlSessionFactory =
            ApplicationContextAwareUtils.getBean(SqlSessionFactory.class);

    /**
     * 执行批量操作,默认每批次执行{@value BATCH_SIZE}条
     *
     * @param list 待执行的整个数据集合
     * @param mapperMethod 单条sql执行的具体实现
     * @param <Entity> 待执行的DO对象的类型
     * @return 成功条数
     * @see SqlBatchUtils#executeBatch(Collection, int, BiConsumer)
     * @since 2022/1/21 17:40
     */
    public static <Entity> int executeBatch(
            Collection<Entity> list, BiConsumer<SqlSession, Entity> mapperMethod) {
        return executeBatch(list, BATCH_SIZE, mapperMethod);
    }

    /**
     * 执行批量操作
     *
     * @param list 待执行的整个数据集合
     * @param batchSize 批次大小
     * @param mapperMethod 单条sql执行的具体实现
     * <pre>
     *     如:{@code (batchSqlSession,recordDO) ->
     *              batchSqlSession.getMapper(IRecordDAO.class).saveRecord(recordDO)}
     * </pre>
     * @param <Entity> 待执行的DO对象类型
     * @return 成功条数
     * @since 3.4.0
     */
    public static <Entity> int executeBatch(
            Collection<Entity> list, int batchSize, BiConsumer<SqlSession, Entity> mapperMethod) {
        Assert.isTrue(batchSize > 0, "批次大小必须大于0");
        if (CollectionUtils.isEmpty(list)) {
            return 0;
        }
        SqlSessionHolder sqlSessionHolder =
                (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
        boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();
        if (sqlSessionHolder != null) {
            SqlSession sqlSession = sqlSessionHolder.getSqlSession();
            //原生无法支持执行器切换,当存在批量操作时,会嵌套两个session的,优先commit上一个session
            //按道理来说,这里的值应该一直为false。
            //如果外层有spring事务,这里不真正提交事务,而是交由spring管理
            sqlSession.commit(!transaction);
        }
        //获取能够批量执行的SqlSession
        SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        try {
            int size = list.size();
            int i = 1;
            for (Entity element : list) {
                mapperMethod.accept(batchSqlSession, element);
                if ((i % batchSize == 0) || i == size) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            //非事务情况下,强制commit
            //如果外层有spring事务,这里不真正提交事务,而是交由spring管理
            batchSqlSession.commit(!transaction);
        } catch (Throwable t) {
            //如果外层有spring事务,这里不真正回滚事务,而是交由spring管理
            batchSqlSession.rollback();
            throw new RuntimeException(t);
        } finally {
            batchSqlSession.close();
        }
        return list.size();
    }
}

使用例子

一定要在在批量操作的方法上加上事务!

class Test{
    @Transactional(rollbackFor = Exception.class)
    public int saveVoucherOperationBatch(List<VoucherOperationDO> list) {
        int result = SqlBatchUtils.executeBatch(list,
                (batchSqlSession, entity) -> {
                    IVoucherOperationDAO mapper = batchSqlSession.getMapper(IVoucherOperationDAO.class);
                    mapper.saveVoucherOperation(entity);
                }
        );
        return result;
    }
}

性能比较

测试

数据库:mysql
连接url加上rewriteBatchedStatements=true
总数:6660条
每批次执行:999条

批量执行方式jdbc批处理mybatis批处理foreach拼接sqlfor循环执行sql
执行时间1300ms1377ms1800ms78423ms

结论

执行速度:
JDBC批处理 略快于 Mybatis批处理 略快于 Foreach拼接sql 远快于 for循环执行sql

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis常会出现批量操作,如批量查询,批量插入,批量修改(replace into)。批量操作要比循环执行效率提升很多。一种常见的方法是使用foreach标签,通过在SQL语句中使用foreach标签,可以循环插入或更新多条数据。 具体用法如下: 1. 首先,需要使用SqlSessionFactory来创建一个可以执行批量操作的SqlSession。可以通过调用openSession方法,同时传入ExecutorType.BATCH参数来创建。 2. 然后,获取对应的Mapper接口,通过该接口可以访问数据库并执行批量操作。 3. 在循环中,通过调用Mapper接口的方法来插入或更新数据。 4. 最后,调用openSession的commit方法来提交批量操作,并关闭SqlSession。 以下是一个示例代码: ```java @Test public void testBatch() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH); long start = System.currentTimeMillis(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); for (int i = 0; i < 1000; i++) { mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1")); } openSession.commit(); long end = System.currentTimeMillis(); System.out.println("执行时长:" + (end - start)); } finally { openSession.close(); } } ``` 在这个示例中,使用foreach标签实现了批量插入1000条员工数据。通过循环调用Mapper接口的addEmp方法,可以将每条数据插入到数据库中。最后,通过commit方法提交批量操作,计算执行时间并打印出来。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [MyBatis执行批量操作](https://blog.csdn.net/m0_52369128/article/details/125389635)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Java企业报表管理系统源码](https://download.csdn.net/download/m0_55416028/88269629)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值