mybatis百万数据写入数据库分页优化分批处理java代码实现

场景描述:

现在公司是sqlserver到mysql,要用代码实现初始化的数据搬迁工作,暴露出来初始化接口

问题描述:

数据库使用的是mybatis框架,对于有的表,例如97个字段,一百多万条数据,直接就内存溢出了。

耗时日志(同时优化了sql语句筛选了数据,如果是百万的表筛选了一半):

本次操作共耗时:约11秒,约0分钟,共50000条数据
本次操作共耗时:约10秒,约0分钟,共50000条数据
本次操作共耗时:约55秒,约0分钟,共440000条数据
本次操作共耗时:约0秒,约0分钟,共115条数据
本次操作共耗时:约0秒,约0分钟,共1986条数据
本次操作共耗时:约0秒,约0分钟,共1986条数据
本次操作共耗时:约0秒,约0分钟,共1335条数据
本次操作共耗时:约0秒,约0分钟,共1335条数据
本次操作共耗时:约0秒,约0分钟,共22条数据

解决办法:

① mapper.xml优化

传入类型为list集合,使用foreach来遍历拼接语句


  
    insert into oa_workflow_flownode (workflowid, nodeid, nodename,
    drawxpos, drawypos, nodetype,
    nodetypeCN, isstart, isreject,
    isend, version, description
    )
    values
    
      (
        #{item.workflowid,jdbcType=VARCHAR}, #{item.nodeid,jdbcType=VARCHAR}, #{item.nodename,jdbcType=VARCHAR},
        #{item.drawxpos,jdbcType=INTEGER}, #{item.drawypos,jdbcType=INTEGER}, #{item.nodetype,jdbcType=INTEGER},
        #{item.nodetypecn,jdbcType=VARCHAR}, #{item.isstart,jdbcType=INTEGER}, #{item.isreject,jdbcType=INTEGER},
        #{item.isend,jdbcType=INTEGER}, #{item.version,jdbcType=CHAR}, #{item.description,jdbcType=VARCHAR}
      )
    
  

② 对需要插入的数据进行分页插入,分页大小自己控制,随手一测就出来,不同的分页使用不同的分页大小

OAController.java中部分代码

    //查询OA系统中的workflow_nodelink表数据,导入到bpm系统中的oa_workflow_nodelink表中
    public void importOAWorkflowNodelink() throws Exception {
        try {
            //查询OA系统中的workflow_nodelink表数据
            List recordList = oaWorkflowNodelinkService.getList();
            //加入UUID,这是id
            for (OAWorkflowNodelink oaWFN : recordList) {
                oaWFN.setId(UUID.randomUUID().toString());
                oaWFN.setVersion("systeminit");
            }
            //清空表
            oaWorkflowNodelinkService.deleteAll();
            //导入到bpm系统中的oa_workflow_flownode表中
            batchProcess(OAWorkflowNodelink.class, 10000, recordList);

        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(this.getClass().getName() + "发生一些错误");
        }
    }

    /**
     * 对于大量数据的插入操作进行分批处理节约时间
     * 效能就会提升很多很多倍
     *
     * @param type     实例class
     * @param pageSize 分页大小
     * @param list     实例的集合
     */
    public void batchProcess(Class type, int pageSize, List list) {

        long start_time = System.currentTimeMillis();//开始执行时间

        //分批数据信息
        int totalSize = list.size(); //总记录数
        int totalPage = totalSize / pageSize; //共N页

        if (totalSize % pageSize != 0) {
            totalPage += 1;
            if (totalSize < pageSize) {
                pageSize = list.size();
            }
        }

        for (int pageNum = 1; pageNum < totalPage + 1; pageNum++) {
            int starNum = (pageNum - 1) * pageSize;
            int endNum = pageNum * pageSize > totalSize ? (totalSize) : pageNum * pageSize;

            if (type == OAWorkflowNodelink.class) {
                oaWorkflowNodelinkService.insertForeach(list.subList(starNum, endNum));
            }

        }

        logger.info("[" + type.getName() + "]本次操作共耗时:约" +
                (System.currentTimeMillis() - start_time) / 1000 + "秒,约" +
                (System.currentTimeMillis() - start_time) / 60000 + "分钟,共" +
                totalPage * pageSize + "条数据");

    }

代码遗憾:

真的很想写一个工具类,然后通过工具类来反射达到自己new对象调用方法,因为每个对象里面的调用的方法都是insertForeach,但是ssm通过工厂了,如果我在工具类中通过new controller层对象的实现方式,那么在service层的dao层的注解注入就没有办法注入成功,除非使用spring-mvc.xml来配置bean,如果这样的话,就太麻烦了,所以我在这里就放弃这样,这会使代码更加复杂难读,毕竟还是要留给后人的,我就不恶心他们了。还是直接写在当前使用的controller下好了。

贴一下,如何通过反射实现调用方法,如果那些方法中没有对象是通过注解方式实现的,就可以使用。

import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 关于此系统的数据库操作工具类
 *
 * @author xiaheshun
 */
public class DBUtils {

    private static Logger logger = LoggerFactory.getLogger(DBUtils.class);

    /**
     * 对于大量数据的插入操作进行分批处理节约时间
     * pagesize越大,插入越快
     * 自己随便通过数据库测一下那个字段能用多大是最佳的
     * 
     * @param type     实例class
     * @param pageSize 分页大小
     * @param list     实例的集合
     */
    public static void batchProcess(Class serviceType, int pageSize, List list) throws Exception {

        long start_time = System.currentTimeMillis();//开始执行时间

        //分批数据信息
        int totalSize = list.size(); //总记录数
        int totalPage = totalSize / pageSize; //共N页

        if (totalSize % pageSize != 0) {
            totalPage += 1;
            if (totalSize < pageSize) {
                pageSize = list.size();
            }
        }

        for (int pageNum = 1; pageNum < totalPage + 1; pageNum++) {
            int starNum = (pageNum - 1) * pageSize;
            int endNum = pageNum * pageSize > totalSize ? (totalSize) : pageNum * pageSize;
            Method insertForeach = serviceType.getMethod("insertForeach", List.class);
            insertForeach.invoke(list.subList(starNum, endNum));
        }

        logger.info("本次操作共耗时:" +
                (System.currentTimeMillis() - start_time) / 1000 + "秒,约" +
                (System.currentTimeMillis() - start_time) / 60000 + "分钟");

    }

}

你可能感兴趣的:(后端)