# 2022年12月26日 - 2.1.0 发布
- Fn支持直接输入字段名或列名,支持使用字符串形式的参数。
- Fn增加方法,当通过基类或基类泛型的字段引用时,可以指定所属的实体类 fixed#50
- 兼容 spring boot 3.0
- 增加h2驱动和相关测试,去掉mysql依赖,完善批量查询功能,支持复合主键的批量更新。
- 所有sql文件开头不能包含开源协议信息,会导致sql执行出错
- 批量更新支持联合主键 by dengsoldeng
- 新增批量更新 updateList、updateListSelective by dengsoldeng
- Example 增加 excludeColumns 排除查询列的方法,ExampleWrapper 增加 exclude 排除查询列的方法。由于重用了之前的 selectColumns 方法,这会导致之前可以多次调用 selectColumns追加查询列变成了每次调用都会覆盖查询列,不想指定时,可以 selectColumns() 空参数设置为空。fixed #63
- ExampleWrapper 增加 clear 方法,方便重用 wrapper, fixed #70
- 解决 mapper-bom 依赖 mapper-parent 导致的依赖管理冲突问题, fixed #67
- 解决 sql 中注释位置错误导致的测试问题
- 修改字段批量操作方法为 Collection 类型后,这是一个不兼容的测试接口方法
- 通过 .editorconfig 统一代码样式,格式化代码
- 修改字段批量操作方法为 Collection 类型,可能会出现方法定义不兼容的情况
# Fn支持直接输入字段名或列名,支持使用字符串形式的参数
由于字段只能通过方法引用方式,这样会导致部分场景无法使用字符串传递字段或列名。为了解决这个问题,Fn 增加了几个方法,可以直接传递字段名或列名,支持使用字符串形式的参数。
Assert.assertEquals("is_admin", Fn.field(UserIs.class, UserIs::isAdmin).toColumn());
Assert.assertEquals("is_admin", Fn.field(UserIs.class, "admin").toColumn());
Assert.assertEquals("is_admin", Fn.column(UserIs.class, "is_admin").toColumn());
第一个方法时之前默认提供的方法,第二个方法允许指定Java字段名,第三个方法允许指定数据库列名。
下面是示例:
users = mapper.selectColumns(user, Fn.of(UserIds.class, "id1", "name"));
users.forEach(u -> {
Assert.assertNotNull(u.getId1());
Assert.assertNull(u.getId2());
Assert.assertNotNull(u.getName());
});
//使用字段名
example.createCriteria().andEqualTo(Fn.field(User.class, "roleId"), user.getRoleId());
//使用列名
example.createCriteria().andEqualTo(Fn.column(User.class, "role_id"), user.getRoleId());
由于字段名和列名都需要指定类型,因此能避免SQL注入。
# 批量更新方法
在 ListMapper<T>
中增加了下面的方法:
/**
* 批量更新
*
* @author dengsd
* @date 2022/9/27 11:49
*/
@Lang(Caching.class)
@UpdateProvider(type = ListProvider.class, method = "updateList")
int updateList(@Param("entityList") List<? extends T> entityList);
/**
* 批量更新
*
* @author dengsd
* @date 2022/9/27 11:49
*/
@Lang(Caching.class)
@UpdateProvider(type = ListProvider.class, method = "updateListSelective")
int updateListSelective(@Param("entityList") List<? extends T> entityList);
上述方法示例(单测):
public class BatchUpdateMapperTest extends H2BaseMapperTest {
@Test
public void testUpdateList() {
SqlSession sqlSession = getSqlSession();
try {
TestBatchUpdateMapper batchUpdateUserIdsMapper = sqlSession.getMapper(TestBatchUpdateMapper.class);
List<UserIds> users = new ArrayList<>(10);
for (int i = 0; i < 2; i++) {
UserIds user = new UserIds();
user.setId1(1L);
user.setId2(i + 1L);
user.setName("测试" + i);
users.add(user);
}
Assert.assertEquals(2, batchUpdateUserIdsMapper.updateList(users));
sqlSession.rollback();
} finally {
//不要忘记关闭sqlSession
sqlSession.close();
}
}
@Test
public void testUpdateListSelective() {
SqlSession sqlSession = getSqlSession();
try {
TestBatchUpdateMapper batchUpdateUserIdsMapper = sqlSession.getMapper(TestBatchUpdateMapper.class);
List<UserIds> users = new ArrayList<>(10);
UserIds user3 = new UserIds();
user3.setId1(1L);
user3.setId2(3L);
user3.setName(null);
users.add(user3);
UserIds beforeData = batchUpdateUserIdsMapper.selectByPrimaryKey(user3).get();
for (int i = 0; i < 2; i++) {
UserIds user = new UserIds();
user.setId1(1L);
user.setId2(i + 1L);
user.setName("测试" + i);
users.add(user);
}
Assert.assertEquals(3, batchUpdateUserIdsMapper.updateListSelective(users));
UserIds afterData = batchUpdateUserIdsMapper.selectByPrimaryKey(user3).get();
Assert.assertEquals(beforeData.getName(), afterData.getName());
sqlSession.rollback();
} finally {
//不要忘记关闭sqlSession
sqlSession.close();
}
}
}
updateList
方法生成的xml代码:
<script>
UPDATE user_ids
<trim prefix="SET" suffixOverrides="," suffix=" ">
<trim prefix="id1 = CASE " suffixOverrides="" suffix="end, ">
<foreach collection="entityList" item="entity" separator=" ">
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2}) THEN #{entity.id1}
</foreach>
</trim>
<trim prefix="id2 = CASE " suffixOverrides="" suffix="end, ">
<foreach collection="entityList" item="entity" separator=" ">
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2}) THEN #{entity.id2}
</foreach>
</trim>
<trim prefix="name = CASE " suffixOverrides="" suffix="end, ">
<foreach collection="entityList" item="entity" separator=" ">
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2}) THEN #{entity.name}
</foreach>
</trim>
</trim>
<where>
(id1,id2) in (
<foreach collection="entityList" item="entity" open="(" close=")" separator="),(">
#{entity.id1},#{entity.id2}
</foreach>
)
</where>
</script>
输出的日志:
17:31:55.707 [main] DEBUG i.m.m.T.updateList - ==> Preparing: UPDATE user_ids SET id1 = CASE WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? end, id2 = CASE WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? end, name = CASE WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? end WHERE (id1,id2) in ( ( ?,? ),( ?,? ) )
17:31:55.786 [main] DEBUG i.m.m.T.updateList - ==> Parameters: 1(Long), 1(Long), 1(Long), 1(Long), 2(Long), 1(Long), 1(Long), 1(Long), 1(Long), 1(Long), 2(Long), 2(Long), 1(Long), 1(Long), 测试0(String), 1(Long), 2(Long), 测试1(String), 1(Long), 1(Long), 1(Long), 2(Long)
17:31:55.791 [main] DEBUG i.m.m.T.updateList - <== Updates: 2
updateListSelective
方法生成的xml代码:
<script>
UPDATE user_ids
<trim prefix="SET" suffixOverrides="," suffix=" ">
<trim prefix="id1 = CASE " suffixOverrides="" suffix="end, ">
<foreach collection="entityList" item="entity" separator=" ">
<choose>
<when test="entity.id1 != null">
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2}) THEN #{entity.id1}
</when>
<otherwise>
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2} ) THEN id1
</otherwise>
</choose>
</foreach>
</trim>
<trim prefix="id2 = CASE " suffixOverrides="" suffix="end, ">
<foreach collection="entityList" item="entity" separator=" ">
<choose>
<when test="entity.id2 != null">
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2}) THEN #{entity.id2}
</when>
<otherwise>
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2} ) THEN id2
</otherwise>
</choose>
</foreach>
</trim>
<trim prefix="name = CASE " suffixOverrides="" suffix="end, ">
<foreach collection="entityList" item="entity" separator=" ">
<choose>
<when test="entity.name != null">
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2}) THEN #{entity.name}
</when>
<otherwise>
WHEN ( id1 = #{entity.id1} AND id2 = #{entity.id2} ) THEN name
</otherwise>
</choose>
</foreach>
</trim>
</trim>
<where>
(id1,id2) in (
<foreach collection="entityList" item="entity" open="(" close=")" separator="),(">
#{entity.id1},#{entity.id2}
</foreach> )
</where>
</script>
输出的日志:
17:31:55.928 [main] DEBUG i.m.m.T.updateListSelective - ==> Preparing: UPDATE user_ids SET id1 = CASE WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? end, id2 = CASE WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? end, name = CASE WHEN ( id1 = ? AND id2 = ? ) THEN name WHEN ( id1 = ? AND id2 = ?) THEN ? WHEN ( id1 = ? AND id2 = ?) THEN ? end WHERE (id1,id2) in ( ( ?,? ),( ?,? ),( ?,? ) )
17:31:55.930 [main] DEBUG i.m.m.T.updateListSelective - ==> Parameters: 1(Long), 3(Long), 1(Long), 1(Long), 1(Long), 1(Long), 1(Long), 2(Long), 1(Long), 1(Long), 3(Long), 3(Long), 1(Long), 1(Long), 1(Long), 1(Long), 2(Long), 2(Long), 1(Long), 3(Long), 1(Long), 1(Long), 测试0(String), 1(Long), 2(Long), 测试1(String), 1(Long), 3(Long), 1(Long), 1(Long), 1(Long), 2(Long)
17:31:55.932 [main] DEBUG i.m.m.T.updateListSelective - <== Updates: 3
# 睿 - 代码生成器已开源
除了命令方式外,可以自己通过单测探索API用法
项目地址: https://github.com/mybatis-mapper/rui