当前位置: 首页 > article >正文

Mybatis中批量插入foreach优化

数据库批量入库方常见方式:Java中foreach和xml中使用foreach

两者的区别:

通过Java的foreach循环批量插入:

当我们在Java通过foreach循环插入的时候,是一条一条sql执行然后将事物统一交给spring的事物来管理(@Transactional),当遇到错误时候可以全部回滚。

缺点:每次执行都会消耗网络io以及磁盘io,执行效率很低。

通过xml中使用foreach批量插入:

在xml中使用foreach会遍历生成一个sql语句然后一次发送到数据库,只需要一次网络 io

如下所示:

<foreach collection="list" item="item" separator=";">
            insert into user(id, name, phone)
            values (#{id}, #{name}, #{phone})
        </foreach>

缺点:如果数据量过大则会生成一个很大的sql,会导致io异常

上诉xml还有优化的写法,如下:

            insert into user(id, name, phone)
            values
            <foreach collection="list" item="item" separator=",">
               (#{id}, #{name}, #{phone})
            </foreach>

两者的区别是在遍历的内容,第一种写法会生成多条单独的插入sql语句(insert into ,,,,,; insert into ....;),第二种是只遍历values后面的内容,使用了insert into .... values 的语法,减少了sql的大小。

除了以上的两种方法之外还可以自己手动实现一个批量插入或修改的工具类(挺好用的)

如下所示:

import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.List;
import java.util.function.BiFunction;

/**
 * @author light pwd
 * @description
 * @date 2024/10/25
 */
@Component
public class MyBatchUtils {
    private static final Logger LOG = LoggerFactory.getLogger(MyBatchUtils.class);
    /**
     * 每次处理1000条
     */
    private static final int BATCH_SIZE = 1000;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 批量处理修改或者插入
     * 变成一条一条的数据,然后最后一起执行。并不是 insertBatch那种方式
     * @param data     需要被处理的数据
     * @param mapperClass  Mybatis的Mapper类
     * @param function 自定义处理逻辑
     * @return int 影响的总行数
     */
    public  <T, U, R> int batchUpdateOrInsert(List<T> data, Class<U> mapperClass, BiFunction<T, U, R> function) {
        int i = 1;
        SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        try {
            U mapper = batchSqlSession.getMapper(mapperClass);
            int size = data.size();
            for (T element : data) {
                function.apply(element, mapper);
                if (i % BATCH_SIZE == 0 || i == size) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            // 非事务环境下强制commit,事务情况下该commit相当于无效(交给spring的事物来管理)
            batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());
        } catch (Exception e) {
            batchSqlSession.rollback();
            LOG.error(e.getMessage());
            throw BusinessException.of(CommonErrorCodes.RUN_TIME_EXCEPTION, "MybatisBatchUtilsException");
        } finally {
            batchSqlSession.close();
        }
        return i - 1;
    }
}

该方法既结合了Java种foreach的优势,又结合了在xml种foreach的优势。这种方式与mybatis-plus的insertBatch是不同的,mybatis-plus默认提供的insertBatch方法本质是一条一条sql执行然后一起提交。

该方法对于大数据量会自动分批插入,每次1000条的插入到数据库,省去了分批处理。

用法也很简单:

        batchUtils.batchUpdateOrInsert(haveIdColumn, PreColumnConfigMapper.class,
                (item, mapper) -> mapper.updateOne(item, createTime, createrId)
        );
        batchUtils.batchUpdateOrInsert(noIdLine, PreLineConfigMapper.class,
                (item, mapper) -> mapper.insertOne(item, createTime, createrId)
        );

其中batchUtils就是通过spring注入进来的MyBatchUtils的bean,haveIdColumn/noIdLine是一个待插入的List数据,PreLineConfigMapper/PreColumnConfigMapper都是mapper文件,

他们得updateOne和insertOne xml方法如下所示(我是pg数据库,mysql是类似的):

  <update id="updateOne" parameterType="com.jiuaoedu.serviceeducation.pre.pojo.PreColumnConfig">
    update service_education.pre_column_config
    set type = #{bean.type,jdbcType=VARCHAR}, grade = #{bean.grade,jdbcType=VARCHAR}, subject = #{bean.subject,jdbcType=VARCHAR},
    name = #{bean.name,jdbcType=VARCHAR}, name_id = #{bean.nameId,jdbcType=VARCHAR},order_num = #{bean.orderNum,jdbcType=INTEGER},
    creater_id = #{createrId,jdbcType=BIGINT},create_time = #{createTime,jdbcType=TIMESTAMP}, target_num = null, class_num = null, del = 1
    where column_id = #{bean.columnId,jdbcType=BIGINT}
  </update>

<insert id="insertOne" parameterType="com.jiuaoedu.serviceeducation.pre.pojo.PreLineConfig">
    insert into service_education.pre_line_config
    (
      plan_id, group_id, title,type, start_date, end_date, start_time, end_time, week, order_num,
      count_date,year,term, time_order, create_time, creater_id
    )
    values
    (
    #{bean.planId,jdbcType=BIGINT},#{bean.groupId,jdbcType=VARCHAR}, #{bean.title,jdbcType=VARCHAR},#{bean.type,jdbcType=VARCHAR},
    #{bean.startDate,jdbcType=TIMESTAMP}, #{bean.endDate,jdbcType=TIMESTAMP},
    #{bean.startTime,jdbcType=TIME},#{bean.endTime,jdbcType=TIME},#{bean.week,jdbcType=VARCHAR},
    #{bean.orderNum,jdbcType=INTEGER}, #{bean.countDate,jdbcType=TIMESTAMP},#{bean.year,jdbcType=INTEGER},
    #{bean.term,jdbcType=VARCHAR}, #{bean.timeOrder,jdbcType=INTEGER},#{createTime,jdbcType=TIMESTAMP}, #{createrId,jdbcType=BIGINT}
    )

  </insert>

 注意事项:当在有事物的方法A中使用MyBatchUtils 工具类的时候,工具类的事物是跟方法A同步的,即方法A回滚则执行的所有数据都会回滚

当在没有事物的方法A中使用时:因为MyBatchUtils 会分批次插入所以每批次会有一个事物,提交也是一个批次一起提交,回滚也只回滚一个批次

奋斗不止,进步无止境,让人生在追求中焕发光彩!!!


http://www.kler.cn/a/394174.html

相关文章:

  • uniapp在app模式下组件传值
  • RabbitMQ高效的消息队列中间件原理及实践
  • c++写一个死锁并且自己解锁
  • Linux git-bash配置
  • 丹摩征文活动|丹摩智算平台使用指南
  • win32 / WTL 开发多线程应用,子线程传递大对象给UI线程(主窗口)的方法
  • Jmeter基础篇(22)服务器性能监测工具Nmon的使用
  • zookeeper之节点基本操作
  • Spark 读取 HDFS 文件时 RDD 分区数的确定原理与源码分析
  • ubuntu[无桌面]——使用FileZilla连接本地和虚拟机实现文件共享
  • AI数字人短视频生成--核心源头技术开发
  • StarRocks Summit Asia 2024 全部议程公布!
  • [pyspark] pyspark中如何修改列名字
  • 【机器学习】如何配置anaconda环境(无脑版)
  • 前端(2)——快速入门CSS
  • 证明在无三角形且最大度数为d的图中,随机染色下每个顶点的平均可用颜色数至少为d/3
  • 认证鉴权框架SpringSecurity-2--重点组件和过滤器链篇
  • 华为云分布式缓存服务(DCS)专家深度解析Valkey,助力openEuler峰会
  • zabbix搭建钉钉告警流程
  • 第21课-C++[set和map学习和使用]
  • 【matlab】数据类型01-数值型变量(整数、浮点数、复数、二进制和十六进制)
  • PostgreSQL 逻辑复制
  • 前端三大组件之CSS,三大选择器,游戏网页仿写
  • 编程之路,从0开始:知识补充篇
  • Rocky、Almalinux、CentOS、Ubuntu和Debian系统初始化脚本v9版
  • 【再谈设计模式】建造者模式~对象构建的指挥家