共计 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