# 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