ExampleWrapper 是对 Example 的封装简化

从 1.2.0 版本开始增加了 ExampleWrapper 的用法,相比 Example 使用更简单方便。

BaseMapper 中存在下面的方法:

/**
 * Example 查询封装
 */
default ExampleWrapper<T, I> wrapper() {
  return new ExampleWrapper<>(BaseMapper.this, example());
}

通过 wrapper() 就会返回一个 ExampleWrapper 对象,使用该对象可以操作所有 Example 方法。

# 基本用法

下面是一些示例:

//获取 Mapper,在 Spring 中可以直接注入使用
UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
//查询 "sex=男 and (name like '杨%') 的数量
long count = mapper.wrapper().eq(User::getSex, "男")
  .or(
      c -> c.startsWith(User::getUserName, "杨")
  ).count();
Assert.assertEquals(1, count);
//查询 "sex=男 and (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 连接。

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

# useCondition 用法

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(参数) 方法的区别