Zacard's Notes

创造Lambda风格的mybatis批量操作

mybatis批量操作

mybatis如何批量插入呢?常用的做法如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// UserDao.java
/**
* 批量插入
*/
int batchInsert(@Param("users") List<User> users);
// UserDao.xml
<insert id="batchInsert">
INSERT INTO user (name,password) VALUES
<foreach collection="users" item="user" index="index" separator=",">
(#{user.name},#{user.password})
</foreach>
</insert>

但是,这样有个影藏的bug:当user集合超过一定数量,会导致动态拼接的sql过长而导致执行报错。并且,批量更新就不能使用这种形式了。

使用mybatis批量提交方式

是否有一种通用的写法实现批量操作呢?查看github中mybatis的官方wiki,里面有批量操作的示例(传送门):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Mapper.xml
<insert id="insertName">
insert into names (name) values (#{value})
</insert>
// java code
List<String> names = new ArrayList<String>();
names.add("Fred");
names.add("Barney");
names.add("Betty");
names.add("Wilma");
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
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原则,代码也显得冗长。

使用java8 lambda改造

首先对于sqlsession的资源管理可以使用java7的特性,try-with-resources管理。例如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<String> names = new ArrayList<String>();
names.add("Fred");
names.add("Barney");
names.add("Betty");
names.add("Wilma");
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
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改造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* MyBatis 批量操作
*
* @author zacard
* @since 2016-05-31 09:17
*/
@Component
public class MyBatisBatch {
private static final Logger logger = LoggerFactory.getLogger(MyBatisBatch.class);
@Autowired
private SqlSessionFactory xSqlSessionFactory;
public <T> void doBatch(Class<T> daoClass, Consumer<T> consumer){
Objects.requireNonNull(consumer);
if (xSqlSessionFactory == null) {
logger.error("无法获取mybatis sqlSessionFactory,请检查mybatis配置");
throw XExceptionFactory.create("mybatis配置错误"));
}
try (SqlSession sqlSession = xSqlSessionFactory.openSession(ExecutorType.BATCH)) {
T mapper = sqlSession.getMapper(daoClass);
consumer.accept(mapper);
sqlSession.commit();
} catch (Exception e) {
logger.error("批量操作失败", e);
throw XExceptionFactory.create("批量操作失败");
}
}

客户端使用示例:

1
2
3
4
5
6
7
8
List<Long> userIds = new ArrayList<>();
userIds.add(1L);
userIds.add(2L);
userIds.add(3L);
myBatisBatch.doBatch(UserDao.class, userDao ->
userIds.forEach(userId -> userDao.insert(userId))
);
坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章