首先定义数据库表映射类:
public class MybatisDemo {
private Long id;
private Long count;
private String name;
public Long getId() {
return id;
}
public Long getCount() {
return count;
}
public String getName() {
return name;
}
// setter……
}
此处省略了数据库表映射和set方法。
接下来就是实现example查询的几种方式,核心代码如下:
方式一:普通Example方式(从and方法开始可以实现动态sql拼接)
Example example = new Example(CandidateBrandEntity.class);
example
//.selectProperties("cabId","cabName")
.and().andEqualTo("cabDeleted",0)
.andLike("cabName","%d%");
// 排序
example.orderBy("cabCreatedTime")
/*.desc()*/
.orderBy("cabId").desc();
// 获得结果
List brands = brandEntityMapper.selectByExample(example);
方式二:Criteria方式(可使用criteria完成动态sql拼接)
Example example = new Example(MybatisDemo.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo(“count”, 0)
.andLike(“name”, “%d%”);
example.orderBy(“count”)
//.desc()
.orderBy(“name”).desc();
List demos = mybatisDemoMapper.selectByExample(example);
方式三:Example.builder 方式(其中where从句中内容可以拿出来进行动态sql拼接)
Example example = Example.builder(MybatisDemo.class)
.select(“cabId”,“cabName”)
.where(Sqls.custom().andEqualTo(“count”, 0)
.andLike(“name”, “%d%”))
.orderByDesc(“count”,“name”)
.build();
List demos = mybatisDemoMapper.selectByExample(example);
方式四:Example.builder + Weekend方式,优势:不用输入属性名,避免数据库有变动或输入错误就会出错
//获得seekendsql
WeekendSqls sqls = WeekendSqls.custom();
//可进行动态sql拼接
sqls = sqls.andEqualTo(MybatisDemo::getCount,0).andLike(MybatisDemo::getName,"%d%");
//获得结果
List demos = mybatisDemoMapper.selectByExample(Example.builder(MybatisDemo.class).where(sqls).orderByDesc(“count”,“name”).build());
通用example语法
Example e = new Example(User.class);
Example.Criteria c = e.createCriteria();
//关键字查询部分
String keyword = pageReq.getKeyword();
if (StringUtils.isNotEmpty(keyword)) {
c.orEqualTo("userName", keyword).orEqualTo("policeNo",keyword).orEqualTo("realName",keyword);
}
//部门查询部门
Example.Criteria criteria = e.createCriteria();
criteria.andEqualTo("departmentId", departmentId);
e.and(criteria);
PageHelper.startPage(pageReq.getPageIndex(), pageReq.getPageSize());
List users = userMapper.selectByExample(e);
执行的sql为:
WHERE (
user_name = ?
OR police_no = ?
OR real_name = ?
)
AND (department_id = ?)
总结下来,就是,
每个条件组合(a/b/c) (d)各自创建自己的cirteria,再用and或者or方法去连接
criteria
英 [kraɪ’tɪərɪə] 美 [kraɪ’tɪrɪə]
n. 标准,条件(criterion的复数)
作者这里用 单词criteria 应该表达的是条件的意思,指sql where条件!
criterion的复数
如下GeneratedCriteria类有成员变量criteria,就是Criterion组成的对象数组。
criteria = new ArrayList();
同时Criteria类继承了抽象父类的很多方法,例如
public Criteria andEqualTo(String property, Object value) {
addCriterion(column(property) + " =", value, property(property));
return (Criteria) this;
}
public Criteria andNotEqualTo(String property, Object value) {
addCriterion(column(property) + " <>", value, property(property));
return (Criteria) this;
}
很多这些方法,里面都是调addCriterion传不同的参数,如下addCriterion实现如下,其实就是new Criterion实例传参,添加到数组criteria中。
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
if (notNull) {
throw new MapperException("Value for " + property + " cannot be null");
} else {
return;
}
}
if (property == null) {
return;
}
criteria.add(new Criterion(condition, value));
}
总结: 对于一个一组条件就是一个 由 Criterion对象组成的数组。对于如下这种嵌套的组合,它的实现就是调方法createCriteria,创建一个新的new Criteria实例,一个Criteria实例包含一个Criterion对象数组还包含一个private String andOr;成员变量描述这组条件用 and还是or连接。
WHERE (
user_name = ?
OR police_no = ?
OR real_name = ?
)
AND (department_id = ?)
criterion
英 [kraɪˈtɪəriən] 美 [kraɪˈtɪriən]
n.(评判或作决定的)标准,准则,原则,条件‘’
可以看到 criterion 是前面 criteria的单数。所以这里就是指单个条件信息。
Criterion类组成的数组是 Criteria类的父类的成员变量,采用protected修饰(protected List criteria;)子类(Criteria类)继承该成员变量。
对于Example的内部静态类Criterion ,我们需要分析其成员变量,如下:
它把单个sql条件分成了如下三种情况:
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
各个情况,最终sql拼接如:
注意:这里的condition 通过addCriterion方法看到,都是例如 column(property) + " <" 或column(property) + " between"
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private String andOr;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
总结: Criterion实例就是一个单个的条件,它组成的对象数组,就是一组条件。Criteriond的成员变量中的condition条件 都是 列名 + 操作符 组成,例如: column(property) + " <" 或column(property) + " between"。它把列名加操作符定义为条件变量。 当然有些条件没有 操作符 如 column(property) + " is null"也是 condition。
Example类成员变量oredCriteria,用于封装最终嵌套拼接的条件,它是由Criteria对象组成的对象数组。一个Criteria实例中又包含一个由Criterion对象组成的对象数组。
protected List oredCriteria;
如下,构造函数是实体类的.class,
/**
* 带exists参数的构造方法
*
* @param entityClass
* @param exists - true时,如果字段不存在就抛出异常,false时,如果不存在就不使用该字段的条件
* @param notNull - true时,如果值为空,就会抛出异常,false时,如果为空就不使用该字段的条件
*/
public Example(Class> entityClass, boolean exists, boolean notNull) {
this.exists = exists;
this.notNull = notNull;
oredCriteria = new ArrayList();
this.entityClass = entityClass;
table = EntityHelper.getEntityTable(entityClass);
//根据李领北建议修改#159
propertyMap = table.getPropertyMap();
this.ORDERBY = new OrderBy(this, propertyMap);
}
其中table、和propertyMap定义如下,构造函数,从实体类中获取拿到这些信息。
protected EntityTable table;
//属性和列对应
protected Map propertyMap;
其中有个构造函数比较特殊,传参为Builder
private Example(Builder builder) {
this.exists = builder.exists;
this.notNull = builder.notNull;
this.distinct = builder.distinct;
this.entityClass = builder.entityClass;
this.propertyMap = builder.propertyMap;
this.selectColumns = builder.selectColumns;
this.excludeColumns = builder.excludeColumns;
this.oredCriteria = builder.exampleCriterias;
this.forUpdate = builder.forUpdate;
this.tableName = builder.tableName;
if (!StringUtil.isEmpty(builder.orderByClause.toString())) {
this.orderByClause = builder.orderByClause.toString();
}
}
builder使用
//获得结果
List demos = mybatisDemoMapper.selectByExample(Example.builder(MybatisDemo.class).where(sqls).orderByDesc("count","name").build());
public static Builder builder(Class> entityClass) {
return new Builder(entityClass);
}
Example example = new Example(MybatisDemo.class);
Example.Criteria criteria = example.createCriteria();
使用如上方式使用example,先new Example实例,调实例的createCriteria,其中createCriteria的实现和调用如下,最终返回 new Criteria实例,看可以看到实例化Criteria,还使用了Map
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
criteria.setAndOr("and");
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria(propertyMap, exists, notNull);
return criteria;
}
mapper实例使用类似如下,跟踪selectByExample方法。
List brands = brandEntityMapper.selectByExample(example);
我们的业务Mapper类,继承Mapper类,Mapper类又继承ExampleMapper又继承SelectByExampleMapper,如下,我们可以看到是ExampleProvider类实现,通过ExampleProvider查看
selectByExample的实现:
@RegisterMapper
public interface SelectByExampleMapper {
@SelectProvider(
type = ExampleProvider.class,
method = "dynamicSQL"
)
List selectByExample(Object var1);
}
ExampleProvider的selectByExample的实现,
/**
* 根据Example查询
*
* @param ms
* @return
*/
public String selectByExample(MappedStatement ms) {
Class> entityClass = getEntityClass(ms);
//将返回值修改为实体类型
setResultType(ms, entityClass);
StringBuilder sql = new StringBuilder("SELECT ");
if (isCheckExampleEntityClass()) {
sql.append(SqlHelper.exampleCheck(entityClass));
}
sql.append("distinct ");
//支持查询指定列
sql.append(SqlHelper.exampleSelectColumns(entityClass));
sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));
sql.append(SqlHelper.exampleWhereClause());
sql.append(SqlHelper.exampleOrderBy(entityClass));
sql.append(SqlHelper.exampleForUpdate());
return sql.toString();
}
跟进SqlHelper.exampleWhereClause(),发现它最后返回是maybaits xml中sql语法格式的字符串。
/**
* Example查询中的where结构,用于只有一个Example参数时
*
* @return
*/
public static String exampleWhereClause() {
return "" +
"\n" +
" ${@tk.mybatis.mapper.util.OGNL@andNotLogicDelete(_parameter)}" +
" \n" +
" \n" +
" \n" +
" ${@tk.mybatis.mapper.util.OGNL@andOr(criteria)}" +
" \n" +
" \n" +
" \n" +
" \n" +
" ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +
" \n" +
" \n" +
" ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value}\n" +
" \n" +
" \n" +
" ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +
" \n" +
" \n" +
" ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +
" \n" +
" #{listItem}\n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" \n" +
" " +
" ";
}
总结: 使用方法就是程序流程,它的使用思路就是,根据实体类实例化Example,又根据Example实例,创建Criteria实例,最后调用业务Mapper实例的如selectByExample方法传入example实例,selectByExample方法会根据传参的内容,生成mybatis兼容的sql形式,mybatis会根据mabatis形式sql解析成为数据库能够执行的原始 SQL。
这就是——通用Mapper
参考URL: https://www.jianshu.com/p/5f84624e96bc
Mybatis 解析 SQL 源码分析一
参考URL: https://my.oschina.net/mengyuankan/blog/2874776
Mybatis解析动态sql原理分析
参考URL: https://www.cnblogs.com/fangjian0423/p/mybaits-dynamic-sql-analysis.html
[推荐]【Mybatis源码分析】06-SqlSession执行过程之获取BoundSql代理对象
参考URL: https://blog.csdn.net/shenchaohao12321/article/details/80008304