【總結】Mybatis-常用的三種批量插入操作

最近在項目當中,有用到批量新增的操作。總結一下,大概有三種方式來完成這個操作,(1)在業務代碼中循環逐條新增(2)在業務代碼中循環逐漸新增-開啓batch模式(3)使用Mybatis-foreach標籤拼接sql執行,逐條更新操作是在數據庫中執行的,在業務代碼中體現的是一次性更新。下面將通過本地連接MySQL數據庫的方式,測試三種方式之間的差異。

 表結構

CREATE TABLE IF NOT EXISTS `role` (
  `id` bigint(20) NOT NULL COMMENT 'id',
  `name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名稱',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

 一、在業務代碼中循環插入

public void inert() {
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.SIMPLE,false);
        RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
        Long start = System.currentTimeMillis();
        Role role;
        for (int i = 0; i < 10000; i++) {
            role = new Role();
            role.setId(i);
            role.setName("name" + i);
            roleMapper.insert(role);
        }
        log.info("業務代碼循環插入10000條數據耗時={}" + (System.currentTimeMillis() - start));
    }

 對應Mybatis-Mapper配置

<insert id="insert" parameterType="com.example.transaction.dto.Role" >
    insert into role (id, name)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR})
  </insert>

二、在業務代碼中循環新增-開啓批處理

public void batchInsert() {
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
        Long start = System.currentTimeMillis();
        Role role;
        for (int i = 0; i < 10000; i++) {
            role = new Role();
            role.setId(i);
            role.setName("name" + i);
            roleMapper.insert(role);
        }
        sqlSession.commit();
        log.info("(使用批處理)業務代碼循環插入1000000條數據耗時={}" + (System.currentTimeMillis() - start));
    }

對應的mapper配置一樣

<insert id="insert" parameterType="com.example.transaction.dto.Role" >
    insert into role (id, name)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR})
  </insert>

另外,jdbc連接時是加上rewriteBatchedStatements=true纔是真正開啓批處理。

spring:
  datasource:
    name: test
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

總結:Mybatis的ExecutorType有三種(REUSE、SIMPLE、BATCH),默認是SIMPLE,這種模式會爲每個語句的執行創建新的預處理,單條提交。而BATCH模式把SQL語句發送到數據庫後,數據庫預處理SQL,只打印一次SQL語句,多次設置參數。

三、Mybatis-foreach-sql拼接方式

@GetMapping("insert")
    public void inert(@RequestParam("count") Integer count) {
        Long start = System.currentTimeMillis();
        List<Role> list = new ArrayList<>();
        Role role;
        for (int i = 0; i < count; i++) {
            role = new Role();
            role.setId(i);
            role.setName("name" + i);
            list.add(role);
        }
        roleMapper.insertBatch(list);
        log.info("sql使用foreach方式循環插入{}條數據耗時={}", count, (System.currentTimeMillis() - start));
    }

SQL語句如下:

<insert id="insertBatch" parameterType="java.util.List" >
    insert into role (id, name)
    values
    <foreach collection="list" item="role" separator=",">
      (#{role.id,jdbcType=INTEGER}, #{role.name,jdbcType=VARCHAR})
    </foreach>
  </insert>

 關於foreach標籤的使用

item:將當前遍歷出的元素賦值給指定的變量
index:索引。遍歷list的時候是index就是索引,item就是當前值
遍歷map的時候index表示的就是map的key,item就是map的值
#{變量名}就能取出變量的值也就是當前遍歷出的元素

在本次測試過程中,使用的數據庫版本是5.7.19,單條sql的最大數據量是4194304,故當數據量太大時,需調整MySQL的配置。(1)可通過MySQL安裝目錄下的my.ini文件修改單條sql的數據量,在‘mysqld’段上添加max_allowed_packet = 20M,(2)或者通過命令行set global max_allowed_packet = 20*1024*1024,然後退出命令行,重啓MySQL服務即可。

在這次測試過程中,當插入100萬條數據時,就會有問題,需調整配置。

四、結果比較 

毫秒 業務代碼循環新增 batch批處理 foreach-sql拼接
10000條 17057 495 576
100000條 159297 1095 3265
1000000條 1912256 17666 36304

總結:當數據量比較大時,使用BATCH批處理方式執行批量操作的效率會高很多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章