常用的做法如下代码所示:
// UserDao.java
/**
* 批量插入
*/
int batchInsert(@Param("users") List users);
// UserDao.xml
INSERT INTO user (name,password) VALUES
(#{user.name},#{user.password})
但是,这样有个影藏的bug:当user集合超过一定数量,会导致动态拼接的sql过长而导致执行报错。并且,批量更新就不能使用这种形式了。
使用mybatis批量提交方式
// Mapper.xml
insert into names (name) values (#{value})
// java code
List names = new ArrayList();
names.add("Fred");
names.add("Barney");
names.add("Betty");
names.add("Wilma");
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false);
try {
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
for (String name : names) {
mapper.insertName(name);
}
sqlSession.commit();
} finally {
sqlSession.close();
}
这种java代码形式的批量操作更加通用且直观。但是需要自己管理sqlsession,这部分自我管理sqlsession的代码不但容易忘记(特别是finally中的sqlsession.close),而且违反了DRY原则,代码也显得冗长。
这里需要注意的是:自我管理sqlsession需要看一下源代码分析
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
可以观察到主要有四种参数类型,分别是
- Connection connection
- ExecutorType execType
- TransactionIsolationLevel level
- boolean autoCommit
官方文档中对这些参数也有详细的解释:
SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。通常来说,如何决定是你 选择下面这些方法时:
Transaction (事务): 你想为 session 使用事务或者使用自动提交(通常意味着很多 数据库和/或 JDBC 驱动没有事务)?
Connection (连接): 你想 MyBatis 获得来自配置的数据源的连接还是提供你自己
Execution (执行): 你想 MyBatis 复用预处理语句和/或批量更新语句(包括插入和 删除)?
所以根据需求选择即可,由于我们要做的事情是批量insert,所以我们选择SqlSession openSession(ExecutorType execType, boolean autoCommit)
首先对于sqlsession的资源管理可以使用java7的特性,try-with-resources管理。例如以下代码:
List names = new ArrayList();
names.add("Fred");
names.add("Barney");
names.add("Betty");
names.add("Wilma");
//这里使用了try-with-resource
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false)) { //在try条件
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
for (String name : names) {
mapper.insertName(name);
}
sqlSession.commit();
} catch (Exception e) {
logger.error("批量操作失败", e);
}
然而,获取sqlsession和sql.commit等部分仍然是重复代码。调用者可能并不关心这些实现细节。
将批量操作独立为一个工具类,并使用java8 lambda改造:
/**
* MyBatis 批量操作
*
* @author jaryle
* @since 2016-05-31 09:17
*/
@Component
public class MyBatisBatch {
private static final Logger logger = LoggerFactory.getLogger(MyBatisBatch.class);
@Autowired
private SqlSessionFactory sqlSessionFactory;
public void doBatch(Class daoClass, Consumer consumer){
Objects.requireNonNull(consumer);
if (sqlSessionFactory == null) {
logger.error("无法获取mybatis sqlSessionFactory,请检查mybatis配置");
throw XExceptionFactory.create("mybatis配置错误"));
}
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false)) {
T mapper = sqlSession.getMapper(daoClass);
consumer.accept(mapper);
sqlSession.commit();
} catch (Exception e) {
logger.error("批量操作失败", e);
throw XExceptionFactory.create("批量操作失败");
}
}
使用示例:
List userIds = new ArrayList<>();
userIds.add(1L);
userIds.add(2L);
userIds.add(3L);
myBatisBatch.doBatch(UserDao.class, userDao ->
userIds.forEach(userId -> userDao.insert(userId))
);