全屏浏览
缩小浏览
回到页首

mysql基础---->Mybatis的批量插入(一)

  这里面记录一下使用mybatis处理mysql的批量插入的问题,测试有可能不准。只愿世间风景千般万般熙攘过后,字里行间,人我两忘,相对无言。


Mybatis的批量插入

我们的测试主体类是springboot环境中的一个控制器类,重要的代码如下,在我们的测试中Constants.MAX_BATCH_NUMBER = 10000。

@GetMapping("insert")
public void insertBatchData() {
    // 构建一个list,大小为1百万条数据
    long beginCreateList = System.currentTimeMillis();
    List<Map<String, Object>> lists = new ArrayList<>();
    for (int i = 0; i < 100000; i ++) {
        Map<String, Object> map = new HashMap<>();
        map.put("userId", i + "");
        map.put("username", "huhx" + i);
        map.put("password", "124" + i);
        map.put("sex", 1);
        map.put("address", System.currentTimeMillis());
        lists.add(map);
    }
    long endCreateList = System.currentTimeMillis();
    logger.debug("创建一个大小为10万的列表,耗时:" + (endCreateList - beginCreateList)); // 4103

    // 插入数据
    dbSessionTemplateSupport.simpleSqlInsertBatch("user.simpleInsertUserData", lists);
    long endInsertData = System.currentTimeMillis();
    logger.debug("插入10万数据,耗时:" + (endInsertData - endCreateList)); // 49649
}

一、我们每10000条数据提交一次事务

public class DbSessionTemplateSupport extends SqlSessionTemplate {

    public DbSessionTemplateSupport(SqlSessionFactory sqlSessionFactory) {
        super(sqlSessionFactory);
    }

    // 支持批量的插入
    public void baseInsertBatch(String sqlStatement, List<Map<String, Object>> list) {
        SqlSession session = getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
        if (list == null || list.size() < 1) {
            return;
        }
        int listSize = list.size();
        try {
            // 如果提交的列表条数小于提交阀值
            if (listSize <= Constants.MAX_BATCH_NUMBER) {
                for (int i = 0; i < list.size(); i++) {
                    session.insert(sqlStatement, list.get(i));
                }
                session.commit();
            } else {
                for (int i = 0; i < list.size(); ) {
                    session.insert(sqlStatement, list.get(i));
                    i++;
                    if (i % Constants.MAX_BATCH_NUMBER == 0 || i == listSize) {
                        session.commit();
                        session.clearCache();
                    }
                }
            }
        } catch (Exception e) {
            session.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }
    }
}

这种方式处理插入,仍旧比较慢(其实是很慢很慢,可能是我的代码问题,没有统计时间,太慢了)。但是这种方式可以支持oracle,下面的这种方式非常快,但是oracle不支持。


二、采用mysql支持的拼接式插入数据

/**
 * mysql的批量插入方式,oracle不支持。
 *
 * @param sqlStatement
 * @param list
 */
public void simpleSqlInsertBatch(String sqlStatement, List<Map<String, Object>> list) {
    if (list == null || list.size() < 1) {
        return;
    }
    // 如果提交的列表条数小于提交阀值
    List<Map<String, Object>>[] splitLists = CommUtil.splitLists(list, Constants.MAX_BATCH_NUMBER);
    for (List<Map<String, Object>> tempList : splitLists) {
        insert(sqlStatement, tempList);
    }
}

我们对原始的列表进行切割,然后依次的插入。每次的插入都是MAX_BATCH_NUMBER条数据。下面是切割的方法

/**
 * 对一个列表按照splitNum进行分割。
 *
 * @param lists
 * @param splitNum
 * @param <T>
 * @return
 */
public static <T> List<T>[] splitLists(List<T> lists, int splitNum) {
    int listSize;
    if (lists == null || (listSize = lists.size()) < 1) {
        return new ArrayList[0];
    }
    int length = listSize % splitNum == 0 ? listSize / splitNum : listSize / splitNum + 1;
    // 这里面如果用ArrayList,会在50行报错。ArrayList list = new List();这样会报错。
    List<T>[] results = new List[length];
    int fromIndex, toIndex;
    for (int i = 0; i < length; i++) {
        fromIndex = i * splitNum;
        toIndex = (fromIndex + splitNum) > listSize ? listSize : (fromIndex + splitNum);
        results[i] = lists.subList(fromIndex, toIndex);
    }
    return results;
}

插入的sql语句在mybatis中是使用for...each的方式,如下:

<!-- mysql的批量插入方式 -->
<insert id="simpleInsertUserData" parameterType="java.util.List">
    INSERT INTO puser
      (userId, username, password, address, sex)
    VALUES
    <foreach collection ="list" item="item" index= "index" separator =",">
        (
        #{item.userId},
        #{item.username},
        #{item.password},
        #{item.address},
        #{item.sex}
        )
    </foreach >
</insert>

10万条数据的分割时间加上插入到mysql数据库,这种方式耗时:15658毫秒。需要注意的是如果常数设置为10万条,也就是第10万插入一次。这种方式会报错的。


posted @ 2018-09-15 15:51  huhx  阅读(7302)  评论(0编辑  收藏  举报