mybatis面试之批量插入
写在前面
本文看下通过mybatis如何来批量插入数据。
1:mybatis提供的数据插入方式
在org.apache.ibatis.session.ExecutorType
类中定义了执行器的种类,因为执行器就是来负责执行sql语句的,所以其也代表了mybatis所支持的数据插入的方式,源码如下:
org.apache.ibatis.session.ExecutorType
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
其中SIMPLE就是默认的,即单条的更新。REUSE就是ReuseExecutor,根据sql语句缓存Statement。BATCH就是BatchExecutor,这种方式可以在一个事务中执行多个语句,然后批量发送给数据库,并提交事务。像这种:
start transaction;
update xxx;
insert xxx;
update yyy;
delete zzz;
commit;
JDBC对应代码如下:
String sql = "INSERT INTO saas_init_bakeups (" +
" delete_number, table_name, data, organ_sign,create_user)" +
" VALUES(?,?,?,?,?,?)";
DataSource dataSource = jdbcTemplate.getDataSource();
Connection connection = dataSource.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
List<Long> list = new ArrayList<>();
try {
connection.setAutoCommit(false);
ps = connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
for (InitBakeupsPo obj : bakeupsPoList) {
ps.setString(1, obj.getDeleteNumber());
ps.setString(2, obj.getTableName());
ps.setBlob(3, string2Blob(obj.getData()));
ps.setString(4, obj.getOrganSign());
ps.setDate(5, date2Sql(obj.getCreateTime()));
ps.setString(6, obj.getCreateUser());
ps.addBatch();
}
ps.executeBatch();
connection.commit();
rs = ps.getGeneratedKeys(); //获取结果
while (rs.next()) {
list.add(rs.getLong(1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
connection.close();
ps.close();
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
分别来看下。
2:SIMPLE使用foreach方式
可能配置如下:
<insert id="addEmps">
insert into tbl_employee(<include refid="insertColumn"></include>)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
但最终还是生成一个如下的sql语句:
insert into tbl_employee(
last_name,email,gender,d_id
)
values
(?,?,?,?)
,
(?,?,?,?)
当foreach循环数据较多时,插入数的时长会增加比较多,可能如下图:
所以使用这种方式时,建议分批插入,每批不超过200条。
3:BATCH
打开session时需要指定参数ExecutorType.BATCH,代码可能如下:
public void test01() {
//指定ExecutorType.BATCH创建sqlsession
SqlSession openSession = build.openSession(ExecutorType.BATCH);
UserDao mapper = openSession.getMapper(UserDao.class);
longstart = System.currentTimeMillis();
for(inti = 0; i < 1000000; i++) {
String name = UUID.randomUUID().toString().substring(0, 5);
mapper.addUser(newUser(null, name, 13));
}
openSession.commit();
openSession.close();
longend = System.currentTimeMillis();
System.out.println("耗时时间:"+(end-start));
}
或
SqlSession session = sessionFactory.openSession(ExecutorType.BATCH);
for (Model model : list) {
session.insert("insertStatement", model);
}
session.flushStatements();
如果有批量需求,建议使用这种方式。
4:Dynamic Sql
这是在Dynamic sql中提供的功能,需要额外引入如下pom:
<dependency>
<groupId>org.mybatis.dynamic-sql</groupId>
<artifactId>mybatis-dynamic-sql</artifactId>
<version>1.1.4</version>
</dependency>
代码可能如下:
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
TableMapper mapper = session.getMapper(TableMapper.class);
//自定义你的方法来获取需要插入的数据
List<TableRecord> records = getRecordsToInsert();
//BatchInsert
BatchInsert<TableRecord> batchInsert = insert(records)
.into(table)
.map(id).toProperty("id")
.map(field1).toProperty("field1")
.map(field2).toProperty("field2")
.build()
.render(RenderingStrategy.MYBATIS3);
batchInsert.insertStatements().stream().forEach(mapper::insert);
session.commit();
} finally {
session.close();
}
写在后面
参考文章列表
mybatis批量插入优化(ExecutorType.BATCH/BatchInsert/executeBatch) 。
MyBatis源码-解读Executor的三个实现类之ReuseExecutor(重用执行器) 。
MyBatis中批量操作foreach与BatchExecutor使用详解 。