通用Mapper Example类使用以及源码分析

目录

  • 一、通用Mapper Example类使用
    • 1. 常用使用方式举例
    • 2. 使用方式:条件嵌套组合
  • 二、通用Mapper Example类源码分析
    • 1. 代码细节理解
      • 1.1 Criteria类
      • 1.2 Criterion类
      • 1.3 Example类成员变量oredCriteria
      • 1.4 Examle构造函数
      • 1.5 Examle静态方法builder
    • 2. Example类关系简单分析
      • 2.1 类关系
      • 2.2 使用流程分析
  • 三、参考

一、通用Mapper Example类使用

1. 常用使用方式举例

首先定义数据库表映射类:

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());

2. 使用方式:条件嵌套组合

通用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方法去连接

二、通用Mapper Example类源码分析

1. 代码细节理解

1.1 Criteria类

criteria
英 [kraɪ’tɪərɪə] 美 [kraɪ’tɪrɪə]

n. 标准,条件(criterion的复数)

通用Mapper Example类使用以及源码分析_第1张图片
作者这里用 单词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 = ?)

1.2 Criterion类

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拼接如:

  • noValue无值情况
    什么情况没有value呢?
    比如: column(property) + " is null"和 column(property) + " is not null")
    sql.append(criterion.getAndOr()).append(" ").append(criterion.getCondition());
  • singleValue单值情况
    sql.append(criterion.getAndOr()).append(" ").append(criterion.getCondition()).append(criterion.getValue());
    举例: and
  • betweenValue between值情况
    sql.append(criterion.getAndOr()).append(" “).append(criterion.getCondition()).append(” “).
    append(criterion.getValue()).append(” and ").append(criterion.getSecondValue());
  • listValue list值情况
    条件(in等) + (x1,x2,x3)

注意:这里的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。

1.3 Example类成员变量oredCriteria

Example类成员变量oredCriteria,用于封装最终嵌套拼接的条件,它是由Criteria对象组成的对象数组。一个Criteria实例中又包含一个由Criterion对象组成的对象数组。

    protected List oredCriteria;

1.4 Examle构造函数

如下,构造函数是实体类的.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();
        }
    }

1.5 Examle静态方法builder

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);
    }

2. Example类关系简单分析

2.1 类关系

通用Mapper Example类使用以及源码分析_第2张图片

2.2 使用流程分析

	Example example = new Example(MybatisDemo.class);
	Example.Criteria criteria = example.createCriteria();

使用如上方式使用example,先new Example实例,调实例的createCriteria,其中createCriteria的实现和调用如下,最终返回 new Criteria实例,看可以看到实例化Criteria,还使用了Map propertyMap属性相关信息的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

你可能感兴趣的:(Java后台)