接口 io.mybatis.mapper.example.ExampleMapper<T, E>

Example 类相关的查询方法,Example 中扩展了一些新的方法,因此默认的 Example 查询已经不支持 MBG 生成的 XXExample 对象作为参数。

Example的用法请参考: mapper4 example useage doc (opens new window), 注意:这里的example和mapper4的用法也不完全一样,详细用法请参考example 单元测试 (opens new window)

先看接口中包含的方法:

  • default Example<T> example(): 返回 Example<T> 对象,自己也可以直接 new
  • int deleteByExample(E example): 根据 Example 删除
  • int updateByExample(@Param("entity") T entity, @Param("example") E example): 根据 Example 条件批量更新实体信息
  • int updateByExampleSelective(@Param("entity") T entity, @Param("example") E example);: 根据 Example 条件批量更新实体不为空的字段
  • List<T> selectByExample(E example): 根据 Example 条件批量查询
  • Optional<T> selectOneByExample(E example): 根据 Example 条件查询单个实体
  • int countByExample(E example): 根据 Example 条件查询总数

接口源码中还注释了几个方法,这些方式是用来说明 2.1.3 使用 Java8 默认方法灵活实现通用方法 的特点,这里不重复介绍。

看完接口方法,在看看 Example 对象中增加了那些特殊的字段和方法:

public class Example<T> {
  /**
   * 排序字段
   */
  protected String            orderByClause;
  /**
   * 是否使用 distinct
   */
  protected boolean           distinct;
  /**
   * 指定查询列
   */
  protected String            selectColumns;
  /**
   * 起始 SQL,添加到 SQL 前,注意防止 SQL 注入
   */
  protected String            startSql;
  /**
   * 结尾 SQL,添加到 SQL 最后,注意防止 SQL 注入
   */
  protected String            endSql;
  /**
   * 多组条件通过 OR 连接
   */
  protected List<Criteria<T>> oredCriteria;
  /**
   * 允许Example查询条件为空
   */
  protected boolean           allowCriteriaEmpty;
  //省略其他
}

# 2.3.1 orderByClause

排序字段是默认就有的,但是为了字段使用的安全,增加了额外的赋值方法:

/**
 * 通过方法引用方式设置排序字段
 *
 * @param fn    排序列的方法引用
 * @param order 排序方式
 * @return Example
 */
public Example<T> orderBy(Fn<T, Object> fn, Order order) {
  if (orderByClause == null) {
    orderByClause = "";
  }
  orderByClause += fn.toColumn() + " " + order;
  return this;
}

示例:

example.orderBy(User::getId, Example.Order.DESC);

这会生成 ORDER BY id desc 排序。

# 2.3.2 distinct

去重 distinct 也是默认的。如果设置为 true,查询时就会使用 SELECT DISTINCT ...

# 2.3.3 selectColumns

selectColumns 是新增的,可以用来指定查询列,对应的方法如下:

/**
 * 指定查询列
 *
 * @param fns 方法引用
 */
public Example<T> selectColumns(Fn<T, Object>... fns) {
  if (selectColumns == null) {
    selectColumns = "";
  }
  for (Fn<T, Object> fn : fns) {
    String column = fn.toColumn();
    String field = fn.toField();
    if (selectColumns.length() != 0) {
      selectColumns += ",";
    }
    if (column.equals(field)) {
      selectColumns += column;
    } else {
      selectColumns += column + " AS " + field;
    }
  }
  return this;
}

用法示例:

<User> example = exampleMapper.example();
example.selectColumns(User::getUserName, User::getSex);
//可以多次调用追加查询列
example.selectColumns(User::getId);

指定查询列后,在 SQL 中会变成 select name as userName, sex, id from ...userName 映射的 name)。

# 2.3.4 startSql

新增加的字段,起始 SQL,添加到 SQL 前,注意防止 SQL 注入。

直接通过 set 方法设置值:

/**
 * 设置起始 SQL
 *
 * @param startSql 起始 SQL,添加到 SQL 前,注意防止 SQL 注入
 */
public void setStartSql(String startSql) {
  this.startSql = startSql;
}

这个字段基本上要配合下面的 endSql 字段一起用,下面示例:

example.setStartSql("select * from (");
example.setEndSql(") tmp limit 1");

示例没太大意义,你可以想个更好的例子来更新此处文档。

# 2.3.5 endSql

和上面新增加的字段类似:结尾 SQL,添加到 SQL 最后,注意防止 SQL 注入

也是直接通过 set 方法设置:

/**
 * 设置结尾 SQL
 *
 * @param endSql 结尾 SQL,添加到 SQL 最后,注意防止 SQL 注入
 */
public void setEndSql(String endSql) {
  this.endSql = endSql;
}

示例看前一个字段的。

# 2.3.6 oredCriteria

默认就有的字段,记录查询条件块。

# 2.3.7 allowCriteriaEmpty

这个字段是新增的,这个字段破坏了 Example 的整体结构,主要目的是在使用时决定:是否允许查询条件为空

当 select 查询时,是可以为空的,这种情况就是查询了全表,由于这个字段默认值为 false 不允许,因此你需要 new Example(true) 才行。

当 delete, update 时,默认不允许为空,这可以避免清库和更新全库。这个参数只在下面方法用到:

/**
 * 获取所有条件,当前方法会校验所有查询条件,如果不存在查询条件就抛出异常。
 * <p>
 * 不允许通过 Example 相关方法操作全表!!!
 *
 * @return 条件
 */
public List<Criteria<T>> getOredCriteria() {
  if (!allowCriteriaEmpty) {
    if (oredCriteria.size() == 0) {
      throw new IllegalArgumentException("Example 条件不能为空");
    }
    boolean noCriteria = true;
    for (Criteria<T> criteria : oredCriteria) {
      if (!criteria.getCriteria().isEmpty()) {
        noCriteria = false;
        break;
      }
    }
    if (noCriteria) {
      throw new IllegalArgumentException("Example 条件不能为空");
    }
  }
  return oredCriteria;
}

当获取查询条件时判断,如果没有有效的查询条件,就抛出异常,避免潜在的风险。

# 2.3.8 clear() 方法

最后说一个方法,同一个 Example 对象经过 clear() 后是可以反复使用的:

/**
 * 清除所有设置
 */
public void clear() {
  oredCriteria.clear();
  orderByClause = null;
  distinct = false;
  selectColumns = null;
  startSql = null;
  endSql = null;
  allowCriteriaEmpty = false;
}

特别注意的是 allowCriteriaEmpty,即使你用 Example(true) 创建的,这里也会设置为 false,想要设置 true 可以调用下面方法:

/**
 * 设置是否允许查询条件为空
 *
 * @param allowCriteriaEmpty true允许空,一般用于查询,false不允许空,一般用于修改和删除
 */
public void allowCriteriaEmpty(boolean allowCriteriaEmpty) {
  this.allowCriteriaEmpty = allowCriteriaEmpty;
}

# 2.3.9 简单示例

介绍完所有字段和一个特殊方法,下面看几个 Example 示例:

Example<User> example = new Example();
example.createCriteria().andGreaterThan(User::getId, 10L).andLike(User::getUserName, "殷%");
Assert.assertEquals(3, exampleMapper.deleteByExample(example));

example.clear();
example.createCriteria().andEqualTo(User::getId, 1L);
User user = new User();
user.setId(1L);
user.setUserName("男主角");
Assert.assertEquals(1, exampleMapper.updateByExample(user, example));

剩下的,你需要掌握的就是 andGreaterThanandEqualTo 这类的方法,这些方法通过名字可以直接理解。