spring中使用mybatis实现批量插入

1次阅读

共计 7250 个字符,预计需要花费 19 分钟才能阅读完成。

有 3 种实现方式:foreach,spring 事务,以及 ExecutorType.BATCH.

1. foreach 方式

这种方式实际是对 SQL 语句进行拼接,生成一个长长的 SQL,对很多变量进行绑定。如果数据量不大(1000 个以内),可以用这种方式。如果数据量太大,可能数据库会报错。

  • 定义接口
public interface StudentMapper05 {public void insertStudent(List<Student> studentList);
}
  • 定义 mapper

适用于 Oracle 数据库

<insert id="insertStudent">
    BEGIN
    <foreach collection="list" item="student" index="index" separator="">
        INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) 
        VALUES
        (SEQ_ID.nextval, #{student.name}, #{student.branch}, #{student.percentage}, #{student.phone}, #{student.email});
    </foreach>
    END;
</insert>

这个 mapper 的含义,就是把上送的 studentList 拼接成一个长 SQL,拼成的 SQL 类似:

BEGIN
INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) VALUES (SEQ_ID.nextval, ?, ?, ?, ?, ?);
INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) VALUES (SEQ_ID.nextval, ?, ?, ?, ?, ?);
INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) VALUES (SEQ_ID.nextval, ?, ?, ?, ?, ?);
...
END;

studentList 有几个,就会生成多少个 insert 语句拼接到一起,每个? 都会进行变量绑定,所以当 studentList 中数据量较多时,生成的 SQL 会很长,导致数据库执行报错。

  • dao
public class StudentDao05 {
    private StudentMapper05 studentMapper;  // 省略 getter 和 setter
    
    public void insertStudentList(List<Student> studentList) {studentMapper.insertStudent(studentList);
    }
}
  • beans

mybatis-spring-05.xml:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="oracleDataSource" />
    <property name="configLocation" value="classpath:mybatis/config/mybatis-config-05.xml"/>
</bean>
<bean id="studentMapper05" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.ws.experiment.spring.mybatis.mapper.StudentMapper05" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="studentDao05" class="com.ws.experiment.spring.mybatis.dao.StudentDao05">
    <property name="studentMapper" ref="studentMapper05" />
</bean>
  • main 函数
public static void main(String[] args) {String[] configFiles = new String[]{"spring-beans-config.xml", "mybatis/mybatis-spring-05.xml"};    // 分别配置 datasource 和 mybatis 相关 bean
    ApplicationContext context = new ClassPathXmlApplicationContext(configFiles);
    
    StudentDao05 studentDao = (StudentDao05)context.getBean("studentDao05");
    
    int counts[] = new int[]{10, 50, 100, 200, 500, 1000, 2000, 3000, 5000, 8000};
    for (int count : counts) {List<Student> studentList = new ArrayList<>();
        for (int i = 0; i < count; i++) {Student st = new Student();
            st.setName("name");
            st.setBranch("");
            st.setEmail("");
            st.setPercentage(0);
            st.setPhone(0);
            studentList.add(st);
        }
        long startTime = System.currentTimeMillis();
        studentDao.insertStudentList(studentList);
        long endTime = System.currentTimeMillis();
        System.out.println("插入" + count + "笔数据耗时:" + (endTime - startTime) +"ms");
    }
}
  • 测试结果
 插入 100 笔数据耗时: 197 ms
插入 200 笔数据耗时: 232 ms
插入 500 笔数据耗时: 421 ms
插入 1000 笔数据耗时: 650 ms
插入 2000 笔数据耗时: 1140 ms
插入 3000 笔数据耗时: 27113 ms
插入 5000 笔数据耗时: 98213 ms
插入 8000 笔数据耗时: 301101 ms

2. 借助 spring 事务

借助 spring 事务,插入一组数据

  • 开启 spring 事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="oracleDataSource" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />
  • 定义接口
public interface StudentMapper06 {public void insertStudent(@Param("student") Student student);
}
  • mapper
<insert id="insertStudent">
    INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) 
    VALUES
    (SEQ_ID.nextval, #{student.name}, #{student.branch}, #{student.percentage}, #{student.phone}, #{student.email})
</insert>
  • dao
public class StudentDao06 {
    private StudentMapper06 studentMapper;  // 省略 getter 和 setter
    
    @Transactional  // spring 事务控制
    public void insertStudentList(List<Student> students) {for (Student student : students) {studentMapper.insertStudent(student);
        }
    }
}
  • beans
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="oracleDataSource" />
    <property name="configLocation" value="classpath:mybatis/config/mybatis-config-06.xml"/>
</bean>
<bean id="studentMapper06" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.ws.experiment.spring.mybatis.mapper.StudentMapper06" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="studentDao06" class="com.ws.experiment.spring.mybatis.dao.StudentDao06">
    <property name="studentMapper" ref="studentMapper06" />
</bean>
  • main

  • 测试结果
batchInsert001 插入 10 笔数据耗时: 602 ms
batchInsert001 插入 50 笔数据耗时: 196 ms
batchInsert001 插入 100 笔数据耗时: 284 ms
batchInsert001 插入 200 笔数据耗时: 438 ms
batchInsert001 插入 500 笔数据耗时: 944 ms
batchInsert001 插入 1000 笔数据耗时: 1689 ms
batchInsert001 插入 2000 笔数据耗时: 3138 ms
batchInsert001 插入 3000 笔数据耗时: 4427 ms
batchInsert001 插入 5000 笔数据耗时: 7368 ms
batchInsert001 插入 8000 笔数据耗时: 11832 ms

3. 使用 ExecutorType.BATCH

基本原理是 SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);,设置 BATCH 方式的 sqlSession

有三种设置方式:

3.1 在 mybatis 的 config 文件中设置

SqlSessionFactoryBean 中可以配置配置文件:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="oracleDataSource" />
    <property name="configLocation" value="classpath:mybatis/config/mybatis-config-06.xml"/>
</bean>

这个 mybatis 配置文件中,设置 BATCH 方式:

<configuration>
    <settings>
        <!-- 默认打开 BATCH 的 Executor -->
        <setting name="defaultExecutorType" value="BATCH" />
    </settings>
    <mappers>
        <mapper class="com.ws.experiment.spring.mybatis.mapper.StudentMapper06" />
    </mappers>
</configuration>

这样,默认打开的 sqlSession 就都是 BATCH 方式的。再与 spring 的事务结合(参看上一节中的 spring 事务设置),就可以实现批量插入。

测试结果:

batchInsert001 插入 10 笔数据耗时: 565 ms
batchInsert001 插入 50 笔数据耗时: 117 ms
batchInsert001 插入 100 笔数据耗时: 98 ms
batchInsert001 插入 200 笔数据耗时: 106 ms
batchInsert001 插入 500 笔数据耗时: 145 ms
batchInsert001 插入 1000 笔数据耗时: 132 ms
batchInsert001 插入 2000 笔数据耗时: 154 ms
batchInsert001 插入 3000 笔数据耗时: 163 ms
batchInsert001 插入 5000 笔数据耗时: 200 ms
batchInsert001 插入 8000 笔数据耗时: 250 ms

3.2 自己创建 sqlSession,手工 commit

SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)context.getBean("sqlSessionFactory");
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
StudentMapper06 studentMapper = sqlSession.getMapper(StudentMapper06.class);
for (int i = 0; i < count; i++) {Student st = new Student();
    st.setName("name");
    ...
    studentMapper.insertStudent(st);
}
sqlSession.commit();
sqlSession.clearCache();
sqlSession.close();

测试结果:

batchInsert002 插入 10 笔数据耗时: 568 ms
batchInsert002 插入 50 笔数据耗时: 157 ms
batchInsert002 插入 100 笔数据耗时: 132 ms
batchInsert002 插入 200 笔数据耗时: 135 ms
batchInsert002 插入 500 笔数据耗时: 148 ms
batchInsert002 插入 1000 笔数据耗时: 139 ms
batchInsert002 插入 2000 笔数据耗时: 151 ms
batchInsert002 插入 3000 笔数据耗时: 139 ms
batchInsert002 插入 5000 笔数据耗时: 207 ms
batchInsert002 插入 8000 笔数据耗时: 299 ms

3.3 使用 sqlSessionTemplate 在 XML 文件中创建 bean

创建一个 SqlSessionTemplate,然后注入到 MapperFactoryBean 中,生成对应的 mapper:

<!-- 以 ExecutorType.BATCH 方式插入数据库 -->
<bean id="batchSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
    <constructor-arg name="executorType" value="BATCH" />
</bean>
<bean id="studentMapper06_batch" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="com.ws.experiment.spring.mybatis.mapper.StudentMapper06" />
    <property name="sqlSessionTemplate" ref="batchSqlSessionTemplate" />
</bean>
<bean id="studentDao06_batch" class="com.ws.experiment.spring.mybatis.dao.StudentDao06">
    <property name="studentMapper" ref="studentMapper06_batch" />
</bean>

与 spring 的事务结合后(参看上一节中的 spring 事务设置),就可以实现批量插入

测试结果

batchInsert003 插入 10 笔数据耗时: 651 ms
batchInsert003 插入 50 笔数据耗时: 133 ms
batchInsert003 插入 100 笔数据耗时: 124 ms
batchInsert003 插入 200 笔数据耗时: 129 ms
batchInsert003 插入 500 笔数据耗时: 144 ms
batchInsert003 插入 1000 笔数据耗时: 179 ms
batchInsert003 插入 2000 笔数据耗时: 229 ms
batchInsert003 插入 3000 笔数据耗时: 241 ms
batchInsert003 插入 5000 笔数据耗时: 216 ms
batchInsert003 插入 8000 笔数据耗时: 259 ms
正文完
 0