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">
        <!--@mbg.generated-->
        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模式也是最佳的选择

posted on 2022-10-17 22:58  JonRain0625  阅读(4021)  评论(0编辑  收藏  举报