批量操作
# 批量操作
3.5.4 +
版本支持事务需要手动自行控制(默认false)
注意
autoCommit参数在Spring项目下是无效的,具体见SpringManagedTransactionFactory,在原生mybatis下可用
执行返回值为批量处理结果,业务可根据返回值判断是否成功
数据能否写入或更新取决于代码能否正确执行到flushStatements
支持Spring与非Spring项目使用
执行抛出异常为PersistenceException
saveOrUpdate是个比较有争议的方法,个人建议批量操作保持为简单的新增或更新操作
# 类说明
# MybatisBatch<?>
- 泛型为具体 实际数据类型
- sqlSessionFactory可通过容器获取,非Spring容器下可在自行初始化Mybatis时将上下文记录起来
- dataList为实际的批量数据处理 (非空)
# MybatisBatch.Method<?>
实际为BatchMethod, 封装了框架内部操作方法简化调用.
- 泛型为 实际Mapper方法参数需要的类型
- mapperClass为具体的实际Mapper类
# BatchMethod<?>
- 泛型为 实际Mapper方法参数需要的类型
- statementId : 执行的MappedStatement
- parameterConvert: 参数类型转换处理器,当数据类型与实际mapper方法参数不一致时,可以通过类型转换器进行转换
# 使用说明
- 构建MybatisBatch (将数据与sqlSessionFactory绑定起来)
- 构建MybatisBatch.Method (确定执行执行Mapper类方法)
- 执行操作 (执行处理操作,将批量参数转换为实际mapper需要的参数)
# 返回值说明
List<BatchResult>
执行返回每次按照执行MappedStatement + sql分组返回的操作结果
注意
例如批量根据id更新:
假设10条数据, 5条更新一个字段 5条更新两个字段 , 那返回值就是容量为2的List, 里面各存储了五条记录更新情况
update table set c1 = ? where id = ? //只更新一个字段
update table set c1 = ? , c2 = ? where id = ? // 只更新两个字段
mappedStatement: 执行的MappedStatement
sql: 执行sql
parameterObjects: 参数列表
updateCounts[]: 影响行数(与上面的parameterObjects数据一一对应)
# 使用示例
框架提供了一个MybatisBatchUtils进行静态方法调用.
# execute
适用于insert,update,delete操作
# 示例一: 数据类型为实体
List<H2User> userList = Arrays.asList(new H2User(2000L, "测试"), new H2User(2001L, "测试"));
MybatisBatch<H2User> mybatisBatch = new MybatisBatch<>(sqlSessionFactory, userList);
MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);
mybatisBatch.execute(method.insert());
2
3
4
# 示例二: 数据类型为非实体
List<Long> ids = Arrays.asList(120000L, 120001L);
MybatisBatch<Long> mybatisBatch = new MybatisBatch<>(sqlSessionFactory, ids);
MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);
mybatisBatch.execute(method.insert(id -> {
// 将id转换为实体
H2User h2User = new H2User();
h2User.setTestId(id);
return h2User;
}));
2
3
4
5
6
7
8
9
# 示例三: 自定义方法插入(无注解)
// mapper方法(方法参数无注解)
@Insert(
"insert into h2user(name,version) values( #{name}, #{version})"
)
int myInsertWithoutParam(H2User user1);
// 准备数据集合
List<H2User> h2UserList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
h2UserList.add(new H2User("myInsertWithoutParam" + i));
}
MybatisBatch<H2User> mybatisBatch = new MybatisBatch<>(sqlSessionFactory, h2UserList);
MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);
mybatisBatch.execute(method.get("myInsertWithoutParam"));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 示例四: 自定义方法插入(注解)
// 方法参数带注解
@Insert(
"insert into h2user(name,version) values( #{user1.name}, #{user1.version})"
)
int myInsertWithParam(@Param("user1") H2User user1);
// 准备数据集合
List<H2User> h2UserList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
h2UserList.add(new H2User("myInsertWithParam" + i));
}
MybatisBatch<H2User> mybatisBatch = new MybatisBatch<>(sqlSessionFactory, h2UserList);
MybatisBatch.Method<H2User> method = new MybatisBatch.Method<>(H2UserMapper.class);
mybatisBatch.execute(method.get("myInsertWithParam", (user) -> {
// 转换成mapper方法参数
Map<String, Object> map = new HashMap<>();
map.put("user1", user);
return map;
}));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# saveOrUpdate
执行保存或更新
注意
重点注意跨sqlSession下缓存和数据感知问题
saveOrUpdate(BatchMethod<T> insertMethod, BiPredicate<BatchSqlSession, T> insertPredicate, BatchMethod<T> updateMethod)
insertMethod: 指定insert操作处理
insertPredicate: 指定insert操作处理条件
注意
注意这里的BatchSqlSession,使用这个进行查询操作会每次都执行一次flushStatements.
如果在一次处理中,如果有两条记录相同的数据,在跨sqlSession中会执行两次插入导致主键冲突,而共享sqlsesion下会执行一次插入和一次更新
updateMethod: 指定update操作处理
# 跨sqlSession
@Autowired
private H2UserMapper userMapper;
List<H2User> h2UserList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
h2UserList.add(new H2User(Long.valueOf(40000 + i), "test" + i));
}
MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);
new MybatisBatch<>(sqlSessionFactory,h2UserList).saveOrUpdate(
mapperMethod.insert(), // 指定insert方法
((sqlSession, h2User) -> userMapper.selectById(h2User.getTestId()) == null), //判断条件,引用另个mapper方法
mapperMethod.updateById()); // 指定update方法
2
3
4
5
6
7
8
9
10
11
12
13
# 共用sqlSession
List<H2User> h2UserList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
h2UserList.add(new H2User(Long.valueOf(50000 + i), "test" + i));
}
MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);
new MybatisBatch<>(sqlSessionFactory,h2UserList).saveOrUpdate(mapperMethod.insert(), // 指定insert方法
((sqlSession, h2User) -> sqlSession.selectList(mapperMethod.get("selectById").getStatementId(), h2User.getTestId()).isEmpty()), //判断条件,共用sqlSession
mapperMethod.updateById()); // 指定update方法
2
3
4
5
6
7
8
9
# 事务处理
# Spring事务处理示例一
@Autowired
private TransactionTemplate transactionTemplate;
transactionTemplate.execute((TransactionCallback<List<BatchResult>>) status -> {
MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);
// 执行批量插入
MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert());
throw new RuntimeException("出错了");
});
2
3
4
5
6
7
8
9