Zacard's Notes

关于mybatis3.3.1批量插入回写id的实践

背景

今日,注意到mybatis3.3.1正式发布,果断查看了更新内容(传送门)。大致浏览了下,其中有一项喜人的改进:

Support insert multiple rows and write-back id

批量插入支持id回写了!我们知道,以往如果批量插入,需要获取插入后的ids,是需要根据特定条件反查的。但是,有了这个特性了,完全省去了这一多余查询过程。

实践

迫不及待的试了一把。代码如下:

pom依赖:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.4</version>
</dependency>

mapper interface:

1
2
3
4
5
6
7
8
9
10
11
public interface AccountDAO {
/**
* 批量插入账户
*
* @param accounts 账户集合
* @return 成功插入数量
*/
int batchInsert(List<Account> accounts);
}

mapper xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xkeshi.shop.dao.AccountDAO">
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO account
(username, password)
VALUES
<foreach collection="accounts" item="account" index="index" separator="," >
(#{account.username},#{account.password})
</foreach>
</insert>
</mapper>

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@ContextConfiguration(locations = {"classpath*:spring-test.xml" })
@Transactional
public class AccountDAOTest extends AbstractTestNGSpringContextTests {
@Autowired
private AccountDAO accountDAO;
public void testBatchInsert() {
List<Account> accounts = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Account account = new Account();
account.setUsername("测试" + i);
account.setPassword("888");
accounts.add(account);
}
int result = accountDAO.batchInsert(accounts);
Assert.assertEquals(3, result);
for (Account account : accounts) {
Assert.assertNotNull(account.getId());
}
}
}

然而,测试类结果直接报错:

1
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.ExecutorException: Error getting generated key or setting result to parameter object. Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [accounts, param1]

跟踪这项修改的提交记录(传送门
发现src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java
的修改记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private Collection<Object> getParameters(Object parameter) {
Collection<Object> parameters = null;
if (parameter instanceof Collection) {
parameters = (Collection) parameter;
} else if (parameter instanceof Map) {
Map parameterMap = (Map) parameter;
// when you insert a List(or Array).
// If you want retrieve the auto-increment value or default value.
// You should not to use @Param annotations, You must use a single parameter(List or Array).
if (parameterMap.containsKey("collection")) {
parameters = (Collection) parameterMap.get("collection");
} else if (parameterMap.containsKey("list")) {
parameters = (List) parameterMap.get("list");
} else if (parameterMap.containsKey("array")) {
parameters = Arrays.asList((Object[]) parameterMap.get("array"));
}
}
if (parameters == null) {
parameters = new ArrayList<Object>();
parameters.add(parameter);
}
return parameters;
}

也就是说入参的集合名称必须叫“collection”、“list”、“array”才会生效哦!

修改代码,mapper interface:

1
2
3
4
5
6
7
8
9
10
11
public interface AccountDAO {
/**
* 批量插入账户
*
* @param accounts 账户集合
* @return 成功插入数量
*/
int batchInsert(List<Account> list);
}

mapper xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.xkeshi.shop.dao.AccountDAO">
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO account
(username, password)
VALUES
<foreach collection="list" item="account" index="index" separator="," >
(#{account.username},#{account.password})
</foreach>
</insert>
</mapper>

重新运行测试类,成功!

思考

mybatis这项修改的原理就是,当集合或者数组的parameter名称为“collection”、“list”、“array”,同时设置了useGeneratedKeys=”true” ,就会把生成的id值回写到对应集合或者数组中的实体中。

但是通过parameter名称区别是否回写总是不太优雅,个人感觉应该新增一个注解,添加在相应的集合或者数组上,标示这是一个批量插入的实体,是需要回写id到这里的。

总而言之,这是一个方便大家的改动,enjoy吧~

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章