Skip to content

Releases: mybatis-mapper/mapper

1.2.1 发布

17 Apr 15:02
Compare
Choose a tag to compare

2022年4月17日 - 1.2.1 发布

mybatis-mapper 项目升级到 1.2.1

ExampleWrapper 方法增加 boolean useCondition 参数,当值为 true 时,条件生效,反之无效

用法:

mapper.wrapper()
  .eq(StrUtil.isNotEmpty(sex), User::getSex, "女")
  .or(c -> c.gt(User::getId, 40), c -> c.lt(false, User::getId, 10))
  .or()
  .startsWith(User::getUserName, "张")
  .orderByAsc(User::getId).list();

StrUtil.isNotEmpty(sex) 结果为 true 时,该条件有效,为 false 时无效。

有效时输出的 SQL 如下:

SELECT id,name AS userName,sex FROM user WHERE ( sex = ? AND ( ( id > ? ) ) ) OR ( name LIKE ? ) ORDER BY id ASC
-- Parameters: 女(String), 40(Integer), 张%(String)

无效时输出的 SQL 如下:

SELECT id,name AS userName,sex FROM user WHERE ( ( ( id > ? ) ) ) OR ( name LIKE ? ) ORDER BY id ASC
-- Parameters: 40(Integer), 张%(String)

通过这种用法可以保证当需要判断来设置条件时,可以仍然在链式调用中使用。

注意 or()or(参数) 方法的区别,详细用法和区别请看 1.2.0 更新日志

1.2.0 发布

17 Apr 15:01
Compare
Choose a tag to compare

2022年4月9日 - 1.2.0 发布

mybatis-provider 项目升级到 1.1.1

  1. 升级依赖版本
  2. 替换log4j为logback(测试用)
  3. 去掉了一个 System.out.println 输出

mybatis-mapper 项目升级到 1.2.0

  1. 升级依赖版本
  2. 替换log4j为logback(测试用)
  3. Example 增加 set 方法设置要更新的字段,ExampleMapper 增加 updateByExampleSetValues 方法使用设置的 set 更新数据。
  4. 新增加 ExampleWrapper 类,封装了 Example 方法,该类可以通过 BaseMapper.wrapper() 直接获取使用,通过链式调用方便使用。代码示例:

BaseMapper.wrapper() 示例

//获取 Mapper,在 Spring 中可以直接注入使用
UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
//查询 "sex=男 or name like '杨%' 的数量
long count = mapper.wrapper().eq(User::getSex, "男")
  .or(
      c -> c.startsWith(User::getUserName, "杨")
  ).count();
Assert.assertEquals(1, count);
//查询 "sex=男 or (name like '杨%' or (name like '俞%' and name like '%舟')) 的数量
count = mapper.wrapper().eq(User::getSex, "男")
  .or(
      c -> c.startsWith(User::getUserName, "杨"),
      c -> c.startsWith(User::getUserName, "俞").endsWith(User::getUserName, "舟")
  ).count();
Assert.assertEquals(2, count);
//查询 name 和 sex 列,条件为 (name like '杨%' or sex = '男') or id > 1 and id <= 16 and sex = '女' 的数据
List<User> users = mapper.wrapper()
  .select(User::getUserName, User::getSex)
  .or(c -> c.startsWith(User::getUserName, "杨"),
      c -> c.eq(User::getSex, "男"))
  .or()
  .gt(User::getId, 1L)
  .le(User::getId, 16L)
  .eq(User::getSex, "女").list();

//构建的wrapper可以多次使用
//查询条件为 id > 50 or id <= 5 or sex = '女'
ExampleWrapper<User, Long> wrapper = mapper.wrapper()
  .gt(User::getId, 50L)
  .or()
  .le(User::getId, 5L)
  .or()
  .eq(User::getSex, "女");
//使用当前条件获取前5条数据
users = wrapper.top(5);
Assert.assertEquals(5, users.size());
//追加条件后查询数量
count = wrapper.select(User::getSex).distinct().count();
Assert.assertEquals(2, count);

//根据条件"name=张无忌",更新名字和性别
Assert.assertEquals(1, mapper.wrapper()
  .set(User::getUserName, "弓长无忌")
  .set(User::getSex, "M")
  .eq(User::getUserName, "张无忌").update());
//根据条件"sex=M"查询数量
Assert.assertEquals(1, mapper.wrapper().eq(User::getSex, "M").count());

warpper 封装的方法中,相比 Example 增加了 contains, startsWith, endsWith 3个模糊查询方法,原有的 like 和 notLike 仍然需要手动添加 '%'。

  • contains 在两侧加上 %,如 %内容%
  • startsWith 在右侧加上 %,如 内容%
  • endsWith 在左侧加上 %,如 %内容

上面代码中所有方法对应的数据库日志如下:

countByExample - ==>  Preparing: SELECT COUNT( * ) FROM user WHERE ( sex = ? AND ( ( name LIKE ? ) ) )
countByExample - ==> Parameters: 男(String), 杨%(String)
countByExample - <==      Total: 1
countByExample - ==>  Preparing: SELECT COUNT( * ) FROM user WHERE ( sex = ? AND ( ( name LIKE ? ) OR ( name LIKE ? AND name LIKE ? ) ) )
countByExample - ==> Parameters: 男(String), 杨%(String), 俞%(String), %舟(String)
countByExample - <==      Total: 1
selectByExample - ==>  Preparing: SELECT name AS userName,sex FROM user WHERE ( ( ( name LIKE ? ) OR ( sex = ? ) ) ) OR ( id > ? AND id <= ? AND sex = ? )
selectByExample - ==> Parameters: 杨%(String), 男(String), 1(Long), 16(Long), 女(String)
selectByExample - <==      Total: 44
selectByExample - ==>  Preparing: SELECT id,name AS userName,sex FROM user WHERE ( id > ? ) OR ( id <= ? ) OR ( sex = ? )
selectByExample - ==> Parameters: 50(Long), 5(Long), 女(String)
countByExample - ==>  Preparing: SELECT COUNT( distinct sex ) FROM user WHERE ( id > ? ) OR ( id <= ? ) OR ( sex = ? )
countByExample - ==> Parameters: 50(Long), 5(Long), 女(String)
countByExample - <==      Total: 1
updateByExampleSetValues - ==>  Preparing: UPDATE user SET name = ?, sex = ? WHERE ( name = ? )
updateByExampleSetValues - ==> Parameters: 弓长无忌(String), M(String), 张无忌(String)
updateByExampleSetValues - <==    Updates: 1
countByExample - ==>  Preparing: SELECT COUNT( * ) FROM user WHERE ( sex = ? )
countByExample - ==> Parameters: M(String)
countByExample - <==      Total: 1

特别注意 or()or(Function<Example.OrCriteria<T>, Example.OrCriteria<T>>... orParts) 方法。

or() 是后面的条件和前面用 or 连接。

带参数的 or(...) 是一个嵌套的块,块中的每个部分使用 or 连接,块内使用 and,当前 or(...) 这部分和前面是 and 连接。

如下面代码:

mapper.wrapper()
    .eq(User::getSex, "女")
    .or(c -> c.gt(User::getId, 40), c -> c.lt(User::getId, 10)).list();

对应的查询条件为:sex = '女' and (id > 40 or id < 10),运行时输出的 SQL 如下:

selectByExample - ==>  Preparing: SELECT id,name AS userName,sex FROM user WHERE ( sex = ? AND ( ( id > ? ) OR ( id < ? ) ) )
selectByExample - ==> Parameters: 女(String), 40(Integer), 10(Integer)
selectByExample - <==    Columns: ID, USERNAME, SEX
selectByExample - <==        Row: 41, 武青婴, 女
selectByExample - <==        Row: 46, 郭襄, 女
selectByExample - <==        Row: 50, 韩姬, 女
selectByExample - <==        Row: 51, 黄衫女子, 女
selectByExample - <==        Row: 2, 赵敏, 女
selectByExample - <==        Row: 3, 周芷若, 女
selectByExample - <==        Row: 4, 小昭, 女
selectByExample - <==        Row: 5, 殷离, 女
selectByExample - <==        Row: 7, 殷素素, 女
selectByExample - <==      Total: 9

在上面基础上增加一个 or() 如下:

mapper.wrapper()
  .eq(User::getSex, "女")
  .or(c -> c.gt(User::getId, 40), c -> c.lt(User::getId, 10))
  .or()
  .startsWith(User::getUserName, "张").list();

此时的日志如下:

selectByExample - ==>  Preparing: SELECT id,name AS userName,sex FROM user WHERE ( sex = ? AND ( ( id > ? ) OR ( id < ? ) ) ) OR ( name LIKE ? )
selectByExample - ==> Parameters: 女(String), 40(Integer), 10(Integer), 张%(String)
selectByExample - <==    Columns: ID, USERNAME, SEX
selectByExample - <==        Row: 2, 赵敏, 女
selectByExample - <==        Row: 3, 周芷若, 女
selectByExample - <==        Row: 4, 小昭, 女
selectByExample - <==        Row: 5, 殷离, 女
selectByExample - <==        Row: 6, 张翠山, 男
selectByExample - <==        Row: 7, 殷素素, 女
selectByExample - <==        Row: 9, 张三丰, 男
selectByExample - <==        Row: 13, 张松溪, 男
selectByExample - <==        Row: 41, 武青婴, 女
selectByExample - <==        Row: 46, 郭襄, 女
selectByExample - <==        Row: 47, 张君宝, 男
selectByExample - <==        Row: 50, 韩姬, 女
selectByExample - <==        Row: 51, 黄衫女子, 女
selectByExample - <==      Total: 13

这里 or() 是前后两大块之间的 or,SQL 格式化一下:

SELECT id,name AS userName,sex FROM user 
WHERE 
      ( sex = ? AND ( ( id > ? ) OR ( id < ? ) ) ) 
   OR 
      ( name LIKE ? )

or(...) 对应的 AND ( ( id > ? ) OR ( id < ? ) ),是 AND 连接,块内通过 OR 连接。

最后执行的结果是在前面基础上,把所有姓张的也都查询出来了。

1.1.0 发布

03 Jan 11:10
Compare
Choose a tag to compare

2022年1月3日 - 1.1.0 发布 - 新年快乐:tada:

mybatis-provider 项目升级到 1.1.0

  1. 添加 NameConvert 接口,用于转换实体和字段名,默认为驼峰转下划线,可以通过 SPI 覆盖默认实现
  2. @Entity.Column 注解增加 orderByPriority 用于指定多个排序字段时的顺序,值越小优先级越高

mybatis-mapper 项目升级到 1.1.0

  1. 增加 BaseMapper,作为最基础的接口定义,原来的 Mapper 继承 BaseMapper,并且重写 insert 方法为自增
  2. JPA支持不带注解的默认实体和字段,默认情况下使用驼峰转下划线,可以通过SPI覆盖默认NameConvert 接口实现

1.0.4 发布

03 Jan 11:09
Compare
Choose a tag to compare

mybatis-activerecord 兼容 Spring Boot 2.6.0

1.0.3 发布

03 Nov 01:46
Compare
Choose a tag to compare

2021年11月2日 - 1.0.3 发布了:tada::tada::tada:

mybatis-mapper 项目升级为 1.0.3:

  1. mybatis-common 项目中缺少默认语言包,已经将中文设置为默认语言包。
  2. 解决 Example 中的 orderBy 多次时连接字符串错误的 BUG,增加多个方便使用的 orderBy 方法。
  3. 修复 Example.selectColumns 关键词列名的Bug #16 by ydq
  4. Example 系列方法 支持拼接单层级的 OR 条件 #17 by ydq
    尝试性的支持 #15 单层级(不支持 andOr() 条件里面 继续嵌套更细粒度的 andOr() )的 or 条件拼接
    //example:
    UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
    Example<User> example = mapper.example();
    example.createCriteria()
           .andEqualTo(User::getSex,"男")
           .andOr(example.orPart()
           .andLike(User::getUserName,"杨%"),
           example.orPart()
           .andLike(User::getUserName,"俞%")
           .andLike(User::getUserName,"%舟"));
    输出SQL(支持 select 和 update):
    xxx WHERE ( sex = ? AND ( ( name LIKE ? ) OR ( name LIKE ? AND name LIKE ? ) ) )
  5. Fn<T, R>功能优化,增加缓存,支持引用父类方法,示例如下:
    public static class BaseId {
        @Entity.Column(id = true)
        private Long id;
    
        public Long getId() {
          return id;
        }
    
        public void setId(Long id) {
          this.id = id;
        }
    }
    
    public static class BaseEntity extends BaseId {
        @Entity.Column(value = "when_create", jdbcType = JdbcType.TIMESTAMP)
        private Long whenCreate;
    
        public Long getWhenCreate() {
          return whenCreate;
        }
    
        public void setWhenCreate(Long whenCreate) {
          this.whenCreate = whenCreate;
        }
    }
    
    @Entity.Table("sys_user")
    public static class SysUser extends BaseEntity {
        @Entity.Column("name")
        private String userName;
    
        public String getUserName() {
          return userName;
        }
    
        public void setUserName(String userName) {
          this.userName = userName;
        }
    }
    
    @Entity.Table("sys_role")
    public class SysRole extends BaseEntity {
        @Entity.Column("name")
        private String roleName;
    
        public String getRoleName() {
          return roleName;
        }
    
        public void setRoleName(String roleName) {
          this.roleName = roleName;
        }
    }
    
    @Test
    public void testExtends() {
        Assert.assertEquals("id", ((Fn<SysUser, Object>) SysUser::getId).toField());
        Assert.assertEquals("id", ((Fn<SysUser, Object>) SysUser::getId).toColumn());
        Assert.assertEquals("whenCreate", ((Fn<SysUser, Object>) SysUser::getWhenCreate).toField());
        Assert.assertEquals("when_create", ((Fn<SysUser, Object>) SysUser::getWhenCreate).toColumn());
        Assert.assertEquals("userName", ((Fn<SysUser, Object>) SysUser::getUserName).toField());
        Assert.assertEquals("name", ((Fn<SysUser, Object>) SysUser::getUserName).toColumn());
    
        Assert.assertEquals("id", ((Fn<SysRole, Object>) SysRole::getId).toField());
        Assert.assertEquals("id", ((Fn<SysRole, Object>) SysRole::getId).toColumn());
        Assert.assertEquals("whenCreate", ((Fn<SysRole, Object>) SysRole::getWhenCreate).toField());
        Assert.assertEquals("when_create", ((Fn<SysRole, Object>) SysRole::getWhenCreate).toColumn());
        Assert.assertEquals("roleName", ((Fn<SysRole, Object>) SysRole::getRoleName).toField());
        Assert.assertEquals("name", ((Fn<SysRole, Object>) SysRole::getRoleName).toColumn());
    }

1.0.0 正式版发布

03 Nov 01:45
Compare
Choose a tag to compare

2021年8月12日 - 1.0.0 发布了:tada::tada::tada:

文档: https://mapper.mybatis.io

开发过程

2014年11月开源的 tk.mybatis.mapper 算得上是 MyBatis 的一个重要里程碑,从 通用Mapper 开始,
MyBatis 有了真正意义上的通用 DAO 层,增删改查真的不用在手写或者自动生成,极大的方便了开发人员。

通用Mapper 经历过几次大的重构,每次都从底层使用新的思路重新实现,从第一个版本发布到2020年发布的 4.1.5 版本,
一直是兼容性升级,虽然底层变化了很多,集成方式有了变化,但是业务上集成的接口一直兼容。

虽然经历了几次大的重构,但是由于MyBatis内部的机制,导致无法以更方便更简洁的形式来实现通用Mapper,
从2018年就一直想要从根去解决这个问题,在 2019年3月份给 MyBatis 提交的
pr#1391 合并后(对应 3.5.1 版本,最低要求版本),
终于能以更简单的方式来实现通用 Mapper 了。

2019年实现过一版新的通用Mapper,但是感觉不够好。2020年本来是要写新一版的《MyBatis 从入门到精通》的,
写的过程中也在想着如何把通用Mapper结合到书中,在写书的过程中,逐渐形成了一个新的思路,
结果把目标从写书转移到了开源项目(不务正业),由于工作经常 996,
直到2020年底才基本完成新的实现。本来计划在2021年1月份发布正式版,
为了提供一个更灵活的代码生成器(Mybatis 的 MBG太死板,扩展麻烦),
工作重心又转移到了代码生成器,最终实现了一个代码简单,
功能却异常强大的代码生成器 睿Rui(没开源,作为工具包含在当前项目中直接使用),
此时又过去了好几个月,想着在2021年中发布正式版,
为了尝试在新版本中尽可能通过扩展 100% 兼容 tk-mapper,又增加了一些 SPI 扩展接口。
结果一直拖到了8月份。直到这个月才把 1.0.0 发布了。

整个项目从构思到最终发布经历了近3年的时间,虽然已经花了无数的时间,
但是仍然不能保证有不完美的地方,项目初期,大家发现BUG或者有任何想法都可以提 issues。

为什么新版本是 1.0.0?

这是一个全新的版本的,采用了新的包名 io.mybatis,采用了全新的实现,核心代码是独立的 mybatis-provider 项目,在此项目之上形成了 mybaits-mapper,这个版本不需要任何配置(不会再因为配置出错),可以简单快速的集成并使用。

关于新版本的特点,建议通过 快速上手 进行了解。

后续计划

在旧版本 通用Mapper 中,
有大量的新功能和PR都是增加的新的通用方法,这些方法有些是限制很少的通用方法,
也有很多需要特地条件的通用方法,新版本不会轻易增加新的通用方法,
但是会提供一个扩展项目来容纳所有有意义的通用方法,一方面方便有需要的人取用,
一方面可以作为方法扩展的示例。

除了通用方法外,后续会提供针对 spring boot 的简单 demo,方便入门练习或者参考。

再之后会提供一个脚手架(或找一些现成的开源)项目改造为 睿Rui 代码生成器使用的项目模板,可以快速从头创建项目,
可以在项目中方便的生成代码。