首页 > 其他分享 >mybaties 批量插入的最佳模式 :ExecutorType.BATCH 模式

mybaties 批量插入的最佳模式 :ExecutorType.BATCH 模式

时间:2022-10-17 23:15:22浏览次数:55  
标签:mybaties WCS ExecutorType 模式 ID item import batchSqlSession id

1.0 两种插入方式 

  mybaties 中向数据库中插入批量插入数据 ,有两种方法。一种是使用mybaties的批量执行器模式。一种使用sql语句拼接的模式插入。

2.0 使用方式

2.1 oracle sql拼接的方式如下:


<select id="newId" resultType="java.lang.Long" useCache="false" flushCache="true">
select SEQ_INSPECTION_WCS_DETAIL.nextval
from dual
</select>
 

2.1 oracle主键获取

 oracle使用这种拼接的方法主键ID 不能自动生成 ,得使用oracle中的直增序列来实现 , 实现方式提供两种 1,在mapper 中创建一个序列获取方法,然后在java调用该方法来创建实列的id  如下 ,注意要设置  useCache="false" flushCache="true" 不然会一直获取到相同的序列

    <select id="newId" resultType="java.lang.Long" useCache="false" flushCache="true">
        select SEQ_INSPECTION_WCS_DETAIL.nextval
        from dual
    </select>

调用方法设置主键id 然后插入数据 :

    ImqStandardFaiWcsDetailMapper imqStandardFaiWcsDetailMapper;
   public void   create(List<ImqStandardFaiWcsDetail> list ){
       List<ImqStandardFaiWcsDetail> insertData = list.stream().peek(imqStandardFaiWcsDetail -> {
           Long id = imqStandardFaiWcsDetailMapper.newId();
           imqStandardFaiWcsDetail.setId(id);
       }).collect(Collectors.toList());
       imqStandardFaiWcsDetailMapper.batchInsert(insertData);
    }

这种方式在数据特别大的使用会出现异常,比如10万条数据 那么就要获取ID 10w次,和数据库交互10次,消耗了大量的时间。还有一次拼接太多的数据那么网络io将面临问题,超过的一定的界限值,效率直接下降,这种情况,一般的对象,大概每次insert200 条是最好的,那么怎么来解决这个问题?

2.2  使用触发器来创建ID

  创建出发器:

create or replace trigger t1id_tr1
    before insert
    on INSPECTION_WCS_DETAIL
    for each row
begin
    select SEQ_INSPECTION_WCS_DETAIL.currval into :new.ID from dual;
end t1id_tr1;

在mapper中就不用写ID这个字段的代码了,在java代码中直接调用

    <insert id="batchInsert" parameterType="list" useGeneratedKeys="false">
        <[email protected]>
        insert all
        <foreach collection="list" item="item">
        into IMQ_STANDARD_FAI_WCS_DETAIL
        (ID, STANDARD_FAI_ML_ID, WCS_RELATED, WCS_NAME, WCS_DIRECTION, WCS_VARIABLE, WCS_COEFFICIENT,PART_ID )
        values  (
            #{item.id,jdbcType=DECIMAL},
            #{item.standardFaiMlId,jdbcType=DECIMAL},
            #{item.wcsRelated,jdbcType=VARCHAR},
            #{item.wcsName,jdbcType=VARCHAR},
            #{item.wcsDirection,jdbcType=CHAR},
            #{item.wcsVariable,jdbcType=VARCHAR},
            #{item.wcsCoefficient,jdbcType=VARCHAR},
            #{item.partId,jdbcType=DECIMAL}
            )
        </foreach>
        select 1 from dual
    </insert>

但是问题又来了 ,如果我需要再插入后获取到实列的ID改怎么办?这种方法是没法返回ID  的,而且还没有解决一次插入过多数据引起的sql语句过长,造成网络阻塞。

2.3  创建 ExecutorType.BATCH 来执行,使用手动设置ID

  同样编写创建id  的序列方法

    <select id="newId" resultType="java.lang.Long" useCache="false" flushCache="true">
        select SEQ_INSPECTION_WCS_DETAIL.nextval
        from dual
    </select>

  创建insert的statement

<insert id="insertSelective" parameterType="list" useGeneratedKeys="false">
insert into IMQ_STANDARD_FAI_WCS_DETAIL
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
ID,
</if>
......省略字段
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=DECIMAL},
</if>
....省略字段
</trim>
</insert>

创建java 调用代码的工具类 

package com.ewpt.common.utils;

import com.ewpt.common.utils.uuid.OracleSeq;
import com.ewpt.framework.web.domain.BaseEntity;
import com.ewpt.framework.web.domain.PrimaryKeyId;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Collection;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Supplier;

/**
 * @author CLPS
 */
@Component
public class MybatisBatchUtils {
    
    /**
    * 每次处理1000条
    */
    private static final int BATCH_SIZE = 1000;
    private static SqlSessionFactory sqlSessionFactory;
    @Autowired
    public   void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        MybatisBatchUtils.sqlSessionFactory = sqlSessionFactory;
    }

    /**
    * 批量处理修改或者插入
    *
    * @param data     需要被处理的数据
    * @param mapperClass  Mybatis的Mapper类
    * @param function 自定义处理逻辑
    * @return int 影响的总行数
    */
    public  static <T extends PrimaryKeyId,U,R> int batchUpdateOrInsert(Collection<T> data, Class<U> mapperClass, BiFunction<T,U,R> function, Supplier<OracleSeq> seqSupplier) {
        int i = 1;
        SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        try {
            U mapper = batchSqlSession.getMapper(mapperClass);
            OracleSeq oracleSeq = seqSupplier.get();
            int size = data.size();
            for (T element : data) {
                Long id   = oracleSeq.getId();
                element.setKeyId(id);
                if (Objects.equals(id,oracleSeq.getNextVal())){
                    oracleSeq = seqSupplier.get();
                }
                function.apply(element,mapper);
                if ((i % BATCH_SIZE == 0) || i == size) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            // 非事务环境下强制commit,事务情况下该commit相当于无效
            batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());
        } catch (Exception e) {
            batchSqlSession.rollback();
            throw new RuntimeException(e);
        } finally {
            batchSqlSession.close();
        }
        return i - 1;
    }

    public static   <T extends BaseEntity,U,R> int batchUpdateOrInsert(Collection<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相当于无效
            batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());
        } catch (Exception e) {
            batchSqlSession.rollback();
            throw new RuntimeException(e);
        } finally {
            batchSqlSession.close();
        }
        return i - 1;
    }
}

java调用代码 :

        MybatisBatchUtils.batchUpdateOrInsert(InsertData,ImqStandardFaiWcsDetailMapper.class,(item, imqStandardFaiWcsDetailMapper) -> {
            imqStandardFaiWcsDetailMapper.insertSelective(item);
            return 1;
        });

 这种方式插入数据,效率将大大的提高 ,这特是在大数据下有明显的差异

 

3. mysql 拼接方式如下

    <insert id="batchRoleDept" useGeneratedKeys="false">
        insert into sys_role_dept(id, dept_id) 
        <foreach item="item" index="index" collection="list">
            values (#{item.id},#{item.deptId})
        </foreach>
    </insert>

 mysql  的插入拼接比较的简单,在不考虑性能的情况下,可以设置id自增,自动生成主键 ,返回主键ID  。在数据量打的情况使用ExecutorType.BATCH模式也是最佳的选择

标签:mybaties,WCS,ExecutorType,模式,ID,item,import,batchSqlSession,id
From: https://www.cnblogs.com/jonrain0625/p/16801063.html

相关文章

  • 使用PDF Arrange移除PDF文件的“安全模式”
    以前就写过一篇博客《​​使用pdfsam来移除PDF文件的安全模式​​》,在这篇文章中是使用pdfsam来对PDF文件进行某种程度的“破坏”,然后“重建”另一个PDF文件,从而达到移除“......
  • 设计模式
    设计模式之概述篇1、设计模式的本质​ 面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。2、设计模式的目的​ 提......
  • go语言设计模式-里氏代换与依赖倒转原则
    里氏代换LSP:任何抽象类(interface接口)出现的地方都可以用它的实现类进行替换,实际就是虚拟机制,语言级别实现面向对象功能。能用实例和接口的尽可能抽象成接口,然后用子类来......
  • 【Rust 日报】2021-11-24 Rust中的依赖注入设计模式
    三个Rust代码库的故事现在是使用Rust的好时机了吗?Convex的创始团队(从DropBox分离出来的)有使用Rust开发MagicPocket(Dropbox的地理分布式数据存储系统),Nucleus(重写的Dropbox的......
  • web自动化po模式
    PO是什么:PO模式,PageObject的缩写,页面对象,设计框架的思想,分层思想在PO下,应用程序的每一个页面都有一个对应的pageclass每一个pageclass维护着该web页的元素集和操作这些......
  • 多租户模式
    1、简介定义多租户模式:单个产品实例(SaaS)为多个用户提供服务,用户可按需购买使用产品资源,用户数据相互隔离。租户:狭义:系统的使用者,即用户。广义:除了用户,还包括创建......
  • 【设计模式——六原则】
    前言:​在学习设计模式的时候,贯穿二十三个设计模式的始终——设计模式六原则,单一职责原则、开放—封闭原则、依赖倒转原则、迪米特原则、里氏代换原则、合成—聚合复用原则。......
  • 【设计模式之三工厂】
    前言:本文中所讲解的三工厂指的是简单工厂,工厂方法,抽象工厂设计模式,在大话设计模式中,三个工厂最后以三姐妹的身份出现在比赛现场中,本文中将三个工厂联系起来进行学习。内容:......
  • 【设计模式之代理模式】
    前言:今天我们学习的模式为代理模式,见名字如见该模式,代理模式,则提供一个代理,由代理对对象进行访问,暂时先这么理解!通过接下来的学习,大家肯定会对代理模式有一个全面的了解。......
  • 【设计模式之装饰模式】
    前言:装饰模式,属于二十三个设计模式中之一,那么,什么是装饰模式,下面,大家请跟着我一起走进装饰模式,来看看装饰模式:核心:(一)、结构图想知道设计模式吗?那么看了它的UML图,你就一目......