第 4 章 MybatisPlus 条件构造器
1、CRUD API 的思考
对于 BaseMapper 中基本 CRUD 方法的思考
继承了 BaseMapper 接口后,我们只能获得基本的 CRUD 能力,那么对于复杂的 SQL 查询,MybatisPlus 貌似也无能为力,但其实并不是这样的
现有一个需求,我们需要分页查询 t_user
表中,年龄在 18~50 之间性别为男且姓名为 xx的所有用户,这时候我们该如何实现上述需求呢?
MyBatis:需要在 SQL映射文件中编写带条件查询的 SQL,并基于 PageHelper插件完成
分页 . 实现以上一个简单的需求,往往需要我们做很多重复单调的工作。
MP:依旧不用编写 SQL语句,MP提供了功能强大的条件构造器 QueryMapper
和 LambdaQueryWrapper
2、AbstractWrapper
条件构造器的抽象父类:AbstractWrapper
AbstractWrapper
是 QueryWrapper
(LambdaQueryWrapper
) 和 UpdateWrapper
(LambdaUpdateWrapper
) 的父类用于生成 sql 的 where
条件,entity
属性也用于生成 sql 的 where
条件
Wrapper
可理解为实体包装器, 主要用于处理 sql 拼接、排序、实体参数查询等。
/**
* 查询条件封装
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
@SuppressWarnings({"serial", "unchecked"})
public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>> extends Wrapper<T>
implements Compare<Children, R>, Nested<Children, Children>, Join<Children>, Func<Children, R> {
/**
* 占位符
*/
protected final Children typedThis = (Children) this;
/**
* 必要度量
*/
protected AtomicInteger paramNameSeq;
protected Map<String, Object> paramNameValuePairs;
protected SharedString lastSql;
/**
* SQL注释
*/
protected SharedString sqlComment;
/**
* SQL起始语句
*/
protected SharedString sqlFirst;
/**
* ß
* 数据库表映射实体类
*/
private T entity;
protected MergeSegments expression;
/**
* 实体类型(主要用于确定泛型以及取TableInfo缓存)
*/
private Class<T> entityClass;
@Override
public T getEntity() {
return entity;
}
public Children setEntity(T entity) {
this.entity = entity;
return typedThis;
}
allEq
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
个别参数说明
params
: key
为数据库字段名,value
为字段值
null2IsNull
: 为true
则在map
的value
为null
时调用 isNull 方法,为false
时则忽略value
为null
的字段
例子
- 例1:
allEq({id:1,name:"老王",age:null})
—>id = 1 and name = '老王' and age is null
- 例2:
allEq({id:1,name:"老王",age:null}, false)
—>id = 1 and name = '老王'
eq
eq(R column, Object val)
eq(boolean condition, R column, Object val)
- 等于 =
- 例:
eq("name", "老王")
—>name = '老王'
ne
ne(R column, Object val)
ne(boolean condition, R column, Object val)
- 不等于 <>
- 例:
ne("name", "老王")
—>name <> '老王'
gt
gt(R column, Object val)
gt(boolean condition, R column, Object val)
- 大于 >
- 例:
gt("age", 18)
—>age > 18
ge
ge(R column, Object val)
ge(boolean condition, R column, Object val)
- 大于等于 >=
- 例:
ge("age", 18)
—>age >= 18
lt
lt(R column, Object val)
lt(boolean condition, R column, Object val)
- 小于 <
- 例:
lt("age", 18)
—>age < 18
le
le(R column, Object val)
le(boolean condition, R column, Object val)
- 小于等于 <=
- 例:
le("age", 18)
—>age <= 18
between
between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)
- BETWEEN 值1 AND 值2
- 例:
between("age", 18, 30)
—>age between 18 and 30
notBetween
notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)
- NOT BETWEEN 值1 AND 值2
- 例:
notBetween("age", 18, 30)
—>age not between 18 and 30
like
like(R column, Object val)
like(boolean condition, R column, Object val)
- LIKE ‘%值%’
- 例:
like("name", "王")
—>name like '%王%'
notLike
notLike(R column, Object val)
notLike(boolean condition, R column, Object val)
- NOT LIKE ‘%值%’
- 例:
notLike("name", "王")
—>name not like '%王%'
likeLeft
likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)
- LIKE ‘%值’
- 例:
likeLeft("name", "王")
—>name like '%王'
leftRight
likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)
- LIKE ‘值%’
- 例:
likeRight("name", "王")
—>name like '王%'
isNull
isNull(R column)
isNull(boolean condition, R column)
- 字段 IS NULL
- 例:
isNull("name")
—>name is null
isNotNull
isNotNull(R column)
isNotNull(boolean condition, R column)
- 字段 IS NOT NULL
- 例:
isNotNull("name")
—>name is not null
in
in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)
- 字段 IN (value.get(0), value.get(1), …)
- 例:
in("age",{1,2,3})
—>age in (1,2,3)
in(R column, Object... values)
in(boolean condition, R column, Object... values)
- 字段 IN (v0, v1, …)
- 例:
in("age", 1, 2, 3)
—>age in (1,2,3)
notIn
notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)
- 字段 NOT IN (value.get(0), value.get(1), …)
- 例:
notIn("age",{1,2,3})
—>age not in (1,2,3)
notIn(R column, Object... values)
notIn(boolean condition, R column, Object... values)
- 字段 NOT IN (v0, v1, …)
- 例:
notIn("age", 1, 2, 3)
—>age not in (1,2,3)
inSql
inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
- 字段 IN ( sql语句 )
- 例:
inSql("age", "1,2,3,4,5,6")
—>age in (1,2,3,4,5,6)
- 例:
inSql("id", "select id from table where id < 3")
—>id in (select id from table where id < 3)
notInSql
notInSql(R column, String inValue)
notInSql(boolean condition, R column, String inValue)
- 字段 NOT IN ( sql语句 )
- 例:
notInSql("age", "1,2,3,4,5,6")
—>age not in (1,2,3,4,5,6)
- 例:
notInSql("id", "select id from table where id < 3")
—>id not in (select id from table where id < 3)
groupBy
groupBy(R... columns)
groupBy(boolean condition, R... columns)
- 分组:GROUP BY 字段, …
- 例:
groupBy("id", "name")
—>group by id,name
orderByAsc
orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)
- 排序:ORDER BY 字段, … ASC
- 例:
orderByAsc("id", "name")
—>order by id ASC,name ASC
orderByDesc
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
- 排序:ORDER BY 字段, … DESC
- 例:
orderByDesc("id", "name")
—>order by id DESC,name DESC
orderBy
orderBy(boolean condition, boolean isAsc, R... columns)
- 排序:ORDER BY 字段, …
- 例:
orderBy(true, true, "id", "name")
—>order by id ASC,name ASC
having
having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
- HAVING ( sql语句 )
- 例:
having("sum(age) > 10")
—>having sum(age) > 10
- 例:
having("sum(age) > {0}", 11)
—>having sum(age) > 11
func
func(Consumer<Children> consumer)
func(boolean condition, Consumer<Children> consumer)
- func 方法(主要方便在出现if…else下调用不同方法能不断链)
- 例:
func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
or
or()
or(boolean condition)
- 拼接 OR
注意事项:主动调用or
表示紧接着下一个方法不是用and
连接!(不调用or
则默认为使用and
连接)
- 例:
eq("id",1).or().eq("name","老王")
—>id = 1 or name = '老王'
or(Consumer<Param> consumer)
or(boolean condition, Consumer<Param> consumer)
- OR 嵌套
- 例:
or(i -> i.eq("name", "李白").ne("status", "活着"))
—>or (name = '李白' and status <> '活着')
and
and(Consumer<Param> consumer)
and(boolean condition, Consumer<Param> consumer)
- AND 嵌套
- 例:
and(i -> i.eq("name", "李白").ne("status", "活着"))
—>and (name = '李白' and status <> '活着')
nested
nested(Consumer<Param> consumer)
nested(boolean condition, Consumer<Param> consumer)
- 正常嵌套 不带 AND 或者 OR
- 例:
nested(i -> i.eq("name", "李白").ne("status", "活着"))
—>(name = '李白' and status <> '活着')
apply
apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
- 拼接 sql
注意事项:该方法可用于数据库函数 动态入参的params
对应前面applySql
内部的{index}
部分.这样是不会有sql注入风险的,反之会有!
- 例:
apply("id = 1")
—>id = 1
- 例:
apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
- 例:
apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
last
last(String lastSql)
last(boolean condition, String lastSql)
- 无视优化规则直接拼接到 sql 的最后
注意事项:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
- 例:
last("limit 1")
exists
exists(String existsSql)
exists(boolean condition, String existsSql)
- 拼接 EXISTS ( sql语句 )
- 例:
exists("select id from table where age = 1")
—>exists (select id from table where age = 1)
notExists
notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
- 拼接 NOT EXISTS ( sql语句 )
- 例:
notExists("select id from table where age = 1")
—>not exists (select id from table where age = 1)
3、QueryWrapper
1、AbstractWrapper 的实现类之一:QueryWrapper
QueryWrapper
继承自 AbstractWrapper
,自身的内部属性 entity
也用于生成 where
条件
及 LambdaQueryWrapper
,可以通过 new QueryWrapper().lambda()
方法获取
QueryWrapper
使用的是数据库字段,不是 Java属性!
/**
* Entity 对象封装操作类
*
* @author hubin miemie HCL
* @since 2018-05-25
*/
@SuppressWarnings("serial")
public class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>>
implements Query<QueryWrapper<T>, T, String> {
/**
* 查询字段
*/
private final SharedString sqlSelect = new SharedString();
public QueryWrapper() {
this(null);
}
public QueryWrapper(T entity) {
super.setEntity(entity);
super.initNeed();
}
public QueryWrapper(T entity, String... columns) {
super.setEntity(entity);
super.initNeed();
this.select(columns);
}
select
select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
- 设置查询字段
说明: 以上方法分为两类. 第二类方法为: 过滤查询字段(主键除外), 入参不包含 class 的调用前需要wrapper
内的entity
属性有值! 这两类方法重复调用以最后一次为准
- 例:
select("id", "name", "age")
- 例:
select(i -> i.getProperty().startsWith("test"))
2、AbstractWrapper 的实现类之一:LambdaQueryWrapper
在QueryWrapper
中可以获取到获取LambdaQueryWrapper
对象,LambdaQueryWrapper
支持使用 类名 : getXxx()
的方式构造 SQL 语句
/**
* 返回一个支持 lambda 函数写法的 wrapper
*/
public LambdaQueryWrapper<T> lambda() {
return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), sqlSelect, paramNameSeq, paramNameValuePairs,
expression, lastSql, sqlComment, sqlFirst);
}
4、UpdateWrapper
1、AbstractWrapper 的实现类之一:UpdateWrapper
UpdateWrapper
继承自 AbstractWrapper
,自身的内部属性 entity
也用于生成 where 条件
及 LambdaUpdateWrapper
, 可以通过 new UpdateWrapper().lambda()
方法获取
UpdateWrapper
使用的是数据库字段,不是 Java属性!
/**
* Update 条件封装
*
* @author hubin miemie HCL
* @since 2018-05-30
*/
@SuppressWarnings("serial")
public class UpdateWrapper<T> extends AbstractWrapper<T, String, UpdateWrapper<T>>
implements Update<UpdateWrapper<T>, String> {
/**
* SQL 更新字段内容,例如:name='1', age=2
*/
private final List<String> sqlSet;
public UpdateWrapper() {
// 如果无参构造函数,请注意实体 NULL 情况 SET 必须有否则 SQL 异常
this(null);
}
public UpdateWrapper(T entity) {
super.setEntity(entity);
super.initNeed();
this.sqlSet = new ArrayList<>();
}
set
set(String column, Object val)
set(boolean condition, String column, Object val)
- SQL SET 字段
- 例:
set("name", "老李头")
- 例:
set("name", "")
—>数据库字段值变为空字符串 - 例:
set("name", null)
—>数据库字段值变为null
setSql
setSql(String sql)
- 设置 SET 部分 SQL
- 例:
setSql("name = '老李头'")
2、AbstractWrapper 的实现类之一:LambdaUpdateWrapper
在UpdateWrapper
中可以获取到LambdaUpdateWrapper
对象,LambdaUpdateWrapper
支持使用类名 : getXxx()
的方式构造 SQL 语句
/**
* 返回一个支持 lambda 函数写法的 wrapper
*/
public LambdaUpdateWrapper<T> lambda() {
return new LambdaUpdateWrapper<>(getEntity(), getEntityClass(), sqlSet, paramNameSeq, paramNameValuePairs,
expression, lastSql, sqlComment, sqlFirst);
}
5、Wrapper 原理分析
为什么支持链式调用?
Wrapper 条件构造器支持链式调用,比如:lambdaQuery().eq(Entity::getId, value).one();
、update().eq("id", value).remove();
这样的形式
在 AbstractWrapper
类中定义的条件构造方法,返回值都会 Children
类型。Children
为 AbstractWrapper
类的泛型:Children extends AbstractWrapper<T, R, Children>> extends Wrapper<T>
哟,搁这儿套娃呢?不过我们知道 Children
继承了 AbstractWrapper
抽象类就行,因此调用条件构造方法返回的仍然是 AbstractWrapper
类型的对象,这就是解释了为什么 AbstractWrapper
支持链式调用
/**
* 查询条件封装
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
@SuppressWarnings({"serial", "unchecked"})
public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>> extends Wrapper<T>
implements Compare<Children, R>, Nested<Children, Children>, Join<Children>, Func<Children, R> {
@Override
public Children eq(boolean condition, R column, Object val) {
return addCondition(condition, column, EQ, val);
}
@Override
public Children ne(boolean condition, R column, Object val) {
return addCondition(condition, column, NE, val);
}
@Override
public Children gt(boolean condition, R column, Object val) {
return addCondition(condition, column, GT, val);
}
@Override
public Children ge(boolean condition, R column, Object val) {
return addCondition(condition, column, GE, val);
}
@Override
public Children lt(boolean condition, R column, Object val) {
return addCondition(condition, column, LT, val);
}
@Override
public Children le(boolean condition, R column, Object val) {
return addCondition(condition, column, LE, val);
}
@Override
public Children like(boolean condition, R column, Object val) {
return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT);
}
@Override
public Children notLike(boolean condition, R column, Object val) {
return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT);
}
举个栗子:QueryWrapper
继承了 AbstractWrapper
抽象父类,其 Children
泛型为 QueryWrapper<T>
,因此调用 QueryWrapper
的条件构造方法返回的是 QueryWrapper
对象,那肯定也能再继续调用条件构造方法啦~~~
/**
* Entity 对象封装操作类
*
* @author hubin miemie HCL
* @since 2018-05-25
*/
@SuppressWarnings("serial")
public class QueryWrapper<T> extends AbstractWrapper<T, String, QueryWrapper<T>>
implements Query<QueryWrapper<T>, T, String> {
为什么 LambdaQueryWrapper 可以传入类的 getter 方法?
LambdaQueryWrapper
的父类为 AbstractLambdaWrapper
,在 AbstractLambdaWrapper
父类中定义了 columnsToString()
方法:用于将 getter 方法对应的字段名转换为数据库列名
columnsToString()
方法最终会调用 getColumn()
方法:通过 String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
将 lambda 表达式转换为字段名
/**
* Lambda 语法使用 Wrapper
* <p>统一处理解析 lambda 获取 column</p>
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
@SuppressWarnings("serial")
public abstract class AbstractLambdaWrapper<T, Children extends AbstractLambdaWrapper<T, Children>>
extends AbstractWrapper<T, SFunction<T, ?>, Children> {
private Map<String, ColumnCache> columnMap = null;
private boolean initColumnMap = false;
@SuppressWarnings("unchecked")
@Override
protected String columnsToString(SFunction<T, ?>... columns) {
return columnsToString(true, columns);
}
@SuppressWarnings("unchecked")
protected String columnsToString(boolean onlyColumn, SFunction<T, ?>... columns) {
return Arrays.stream(columns).map(i -> columnToString(i, onlyColumn)).collect(joining(StringPool.COMMA));
}
@Override
protected String columnToString(SFunction<T, ?> column) {
return columnToString(column, true);
}
protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
return getColumn(LambdaUtils.resolve(column), onlyColumn);
}
/**
* 获取 SerializedLambda 对应的列信息,从 lambda 表达式中推测实体类
* <p>
* 如果获取不到列信息,那么本次条件组装将会失败
*
* @param lambda lambda 表达式
* @param onlyColumn 如果是,结果: "name", 如果否: "name" as "name"
* @return 列
* @throws com.baomidou.mybatisplus.core.exceptions.MybatisPlusException 获取不到列信息时抛出异常
* @see SerializedLambda#getImplClass()
* @see SerializedLambda#getImplMethodName()
*/
private String getColumn(SerializedLambda lambda, boolean onlyColumn) {
Class<?> aClass = lambda.getInstantiatedType();
tryInitCache(aClass);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
ColumnCache columnCache = getColumnCache(fieldName, aClass);
return onlyColumn ? columnCache.getColumn() : columnCache.getColumnSelect();
}
private void tryInitCache(Class<?> lambdaClass) {
if (!initColumnMap) {
final Class<T> entityClass = getEntityClass();
if (entityClass != null) {
lambdaClass = entityClass;
}
columnMap = LambdaUtils.getColumnMap(lambdaClass);
initColumnMap = true;
}
Assert.notNull(columnMap, "can not find lambda cache for this entity [%s]", lambdaClass.getName());
}
private ColumnCache getColumnCache(String fieldName, Class<?> lambdaClass) {
ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName));
Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]",
fieldName, lambdaClass.getName());
return columnCache;
}
}
6、获取 Wrapper 对象
Wrappers 类:获取各种 Wrapper 对象
public static <T> QueryWrapper<T> query() {
:获取QueryWrapper
对象public static <T> LambdaQueryWrapper<T> lambdaQuery() {
:获取LambdaQueryWrapper
对象public static <T> UpdateWrapper<T> update() {
:获取UpdateWrapper
对象public static <T> LambdaUpdateWrapper<T> lambdaUpdate() {
:获取LambdaUpdateWrapper
对象public static <T> QueryWrapper<T> emptyWrapper() {
:获取EmptyWrapper
对象
/**
* Wrapper 条件构造
*
* @author Caratacus
*/
public final class Wrappers {
/**
* 空的 EmptyWrapper
*/
private static final QueryWrapper<?> emptyWrapper = new EmptyWrapper<>();
private Wrappers() {
// ignore
}
/**
* 获取 QueryWrapper<T>
*
* @param <T> 实体类泛型
* @return QueryWrapper<T>
*/
public static <T> QueryWrapper<T> query() {
return new QueryWrapper<>();
}
/**
* 获取 QueryWrapper<T>
*
* @param entity 实体类
* @param <T> 实体类泛型
* @return QueryWrapper<T>
*/
public static <T> QueryWrapper<T> query(T entity) {
return new QueryWrapper<>(entity);
}
/**
* 获取 LambdaQueryWrapper<T>
*
* @param <T> 实体类泛型
* @return LambdaQueryWrapper<T>
*/
public static <T> LambdaQueryWrapper<T> lambdaQuery() {
return new LambdaQueryWrapper<>();
}
/**
* 获取 LambdaQueryWrapper<T>
*
* @param entity 实体类
* @param <T> 实体类泛型
* @return LambdaQueryWrapper<T>
*/
public static <T> LambdaQueryWrapper<T> lambdaQuery(T entity) {
return new LambdaQueryWrapper<>(entity);
}
/**
* 获取 LambdaQueryWrapper<T>
*
* @param entityClass 实体类class
* @param <T> 实体类泛型
* @return LambdaQueryWrapper<T>
* @since 3.3.1
*/
public static <T> LambdaQueryWrapper<T> lambdaQuery(Class<T> entityClass) {
return new LambdaQueryWrapper<>(entityClass);
}
/**
* 获取 UpdateWrapper<T>
*
* @param <T> 实体类泛型
* @return UpdateWrapper<T>
*/
public static <T> UpdateWrapper<T> update() {
return new UpdateWrapper<>();
}
/**
* 获取 UpdateWrapper<T>
*
* @param entity 实体类
* @param <T> 实体类泛型
* @return UpdateWrapper<T>
*/
public static <T> UpdateWrapper<T> update(T entity) {
return new UpdateWrapper<>(entity);
}
/**
* 获取 LambdaUpdateWrapper<T>
*
* @param <T> 实体类泛型
* @return LambdaUpdateWrapper<T>
*/
public static <T> LambdaUpdateWrapper<T> lambdaUpdate() {
return new LambdaUpdateWrapper<>();
}
/**
* 获取 LambdaUpdateWrapper<T>
*
* @param entity 实体类
* @param <T> 实体类泛型
* @return LambdaUpdateWrapper<T>
*/
public static <T> LambdaUpdateWrapper<T> lambdaUpdate(T entity) {
return new LambdaUpdateWrapper<>(entity);
}
/**
* 获取 LambdaUpdateWrapper<T>
*
* @param entityClass 实体类class
* @param <T> 实体类泛型
* @return LambdaUpdateWrapper<T>
* @since 3.3.1
*/
public static <T> LambdaUpdateWrapper<T> lambdaUpdate(Class<T> entityClass) {
return new LambdaUpdateWrapper<>(entityClass);
}
/**
* 获取 EmptyWrapper<T>
*
* @param <T> 任意泛型
* @return EmptyWrapper<T>
* @see EmptyWrapper
*/
@SuppressWarnings("unchecked")
public static <T> QueryWrapper<T> emptyWrapper() {
return (QueryWrapper<T>) emptyWrapper;
}