前言

对于mybatis ,很多后端开发曾经很相熟了,因为当初大部分公司用的框架就是mybatis,而Mybatis-Plus(简称MP)是一个 Mybatis 的加强工具。(很多公司也在用这个框架)

在我的项目外面,你常常是不是这样书写:(如查问) Wrappers.<Entity>query().lambda().eq(Entity::getXX, entity2.getXX());

网上想找到Mybatis-Plus的文档和案例,其实很简略,在Mybatis-Plus的官网上或者有很多博客上都能找到的。但你有木有相干它是怎么能实现不须要再写xml了(针对写sql),就能针对性的查问/新增/批改/删除的?当你遇到lambda表达式时,会不会想到他是怎么把这个Get办法传入的?上面就来谈谈Mybatis-Plus是怎么应用lambda表达式,主动生成对应的sql语句的。

代码剖析

基于Mybatis-Plus的3.0.6 版本,这个框架用到了工厂模式和组合模式 以及拦挡过滤器模式。

首先:Wrappers.<Entity>query() 或者Wrappers.<Entity>update() 其实就是在创立一个QueryWrapper 或UpdateWrapper。而后调用lambda办法就是创立LambdaUpdateWrapper 或者 LambdaUpdateWrapper

如图,须要重点关注的是Compare(接口)和AbstractWrapper(类),在Compare接口外面。

public interface Compare<This, R> extends Serializable {      default This eq(R column, Object val) {        return this.eq(true, column, val);    }    This eq(boolean condition, R column, Object val);}

这外面的This就是代表就是返回本身(这里字面是这个意思,理论也是这样弄的),在3.3.2版本外面这个This用Children给取代了。

在AbstractWrapper类外面,其实曾经实现了eq办法(如下图),这个类实现我把其余实现接口去掉了,只留下了Compare接口。(这样看起来比拟清晰)

public abstract class AbstractWrapper<T, R, This extends AbstractWrapper<T, R, This>> extends Wrapper<T> implements Compare<This, R>{     public This eq(boolean condition, R column, Object val) {        return this.addCondition(condition, column, SqlKeyword.EQ, val);    }}  

可能你很纳闷为什么eq /ne 这些办法外面能够间接传递lambda的办法援用(Entity:getXX),而不应该是泛型R?
不要焦急。AbstractLambdaWrapper (实现了AbstractWrapper类,此时 AbstractWrapper类的泛型R用接口SFunction来具体化“取代了”,这个SFunction指定了必须是泛型T外面的办法,这点要留神,如果没有指定泛型可能会报Object is not a functional interface 这样的谬误)。

public abstract class AbstractLambdaWrapper<T, This extends AbstractLambdaWrapper<T, This>>    extends AbstractWrapper<T, SFunction<T, ?>, Children> {        //省略}

调用下面的addCondition办法,理论会解析这个"接口",这个是应用流读取,办法在LambdaUtils外面,如果你有须要能够在我的项目中间接应用这个办法,
这就是看源码的益处。其实这部分就是把以后对象的“数据库"对于列存入缓存(map),将对应列和值也就进行存储。以便到最初面生成sql。(其实在mapper层调用办法时)



本人实现这样的性能 (记录解决列和 对象所有的数据库字段 和串写的形式)

这个外面解析lambda等相干工具从mybatis-plus外面挪了进去,局部性能一重写,还原一个无依赖的我的项目。

1.继承接口

package interfaces;import java.io.Serializable;/** * <ul> * <li>Title: Compare</li> * </ul> * @author 程序员ken * @date 2021/4/28 0028 下午 14:48 */public interface Compare<This, R> extends Serializable {    This eq(boolean var1, R var2, Object var3);    default This eq(R column, Object val) {        return this.eq(true, column, val);    }    This ne(boolean var1, R var2, Object var3);    default This ne(R column, Object val) {        return this.ne(true, column, val);    }    This gt(boolean var1, R var2, Object var3);    default This gt(R column, Object val) {        return this.gt(true, column, val);    }    This lt(boolean var1, R var2, Object var3);    default This lt(R column, Object val) {        return this.lt(true, column, val);    }}

2.接口实现类

AbstractWrapper 类 所有外围办法的实现,这里没有判断是不是SFunction,间接强转的,理论我的项目必须要判断哦

package wrapper;/** * <ul> * <li>Title: AbstractWrapper</li> * <li>Description: TODO </li> * </ul> * * @author 程序员ken * @date 2021/4/28 0028 下午 16:23 *///extends Wrapper<T>public abstract class AbstractWrapper<T, R, This extends  AbstractWrapper>        implements Compare<This, R> {    protected MergeSegmentList expression;    protected Map<String, Object> paramNameValuePairs;    public Class<T> entityClass;    private Map<String, String> columnMap = null;    private boolean initColumnMap = false;    public AbstractWrapper() {    }    //理论实现    @Override    public This eq(boolean condition, R column, Object val) {        String fileName = columnToString((SFunction) column);        MergeSegment segment = new MergeSegment();        segment.setColumName(fileName);        segment.setColumValue(val);        segment.setMatchCondition(MatchCondition.EQ);        expression.add(segment);        paramNameValuePairs.putIfAbsent(fileName,val);        return (This)this;    }    @Override    public This ne(boolean condition, R column, Object val) {        String fileName = columnToString((SFunction) column);        MergeSegment segment = new MergeSegment();        segment.setColumName(fileName);        segment.setColumValue(val);        segment.setMatchCondition(MatchCondition.NE);        expression.add(segment);        paramNameValuePairs.putIfAbsent(fileName,val);        return (This)this;    }    @Override    public This gt(boolean condition, R column, Object val) {        String fileName = columnToString((SFunction) column);        MergeSegment segment = new MergeSegment();        segment.setColumName(fileName);        segment.setColumValue(val);        segment.setMatchCondition(MatchCondition.GT);        expression.add(segment);        paramNameValuePairs.putIfAbsent(fileName,val);        return (This)this;    }    @Override    public This lt(boolean condition, R column, Object val) {        String fileName = columnToString((SFunction) column);        MergeSegment segment = new MergeSegment();        segment.setColumName(fileName);        segment.setColumValue(val);        segment.setMatchCondition(MatchCondition.LT);        expression.add(segment);        paramNameValuePairs.putIfAbsent(fileName,val);        return (This)this;    }    /***     * 性能形容:  获取字段信息     * @return: java.lang.String     * @author: 程序员ken     * @date: 2021/4/28 21:34    */    protected String columnToString(SFunction<T, ?> column) {        SerializedLambda resolve = LambdaUtils.resolve(column);        return this.getColumn(resolve);    }    private String getColumn(SerializedLambda lambda) {        String fieldName = resolveFieldName(lambda.getImplMethodName());        if (!this.initColumnMap || !this.columnMap.containsKey(fieldName)) {            String entityClassName = lambda.getImplClassName();            try{                Class<T> aClass = (Class<T>)Class.forName(entityClassName.replaceAll("\\\\", "."));                if(entityClass==null){                    entityClass = aClass;                }                this.columnMap =  getColumnMap(aClass);                //3.0.6 反对 ==>3.3.2 不反对                //this.columnMap = LambdaUtils.getColumnMap(entityClassName);                this.initColumnMap = true;            }catch (Exception ex){            }        }        return fieldName;    }    /**     * 性能形容: 获取以后实体的“数据库”字段     * @param aClass     * @return: java.util.Map<java.lang.String,java.lang.String>     * @author: 程序员ken     * @date: 2021/4/29 0029 下午 12:39    */    protected  Map<String,String> getColumnMap(Class<?> aClass){        Map<String,String> map = new HashMap<String,String>();        //ClassLoader classLoader = aClass.getClassLoader();        Field[] declaredFields = aClass.getDeclaredFields();        TableField tableField =null;        for (Field field:declaredFields) {             tableField = field.getAnnotation(TableField.class);             if(!(tableField!=null && !tableField.exist())){                 map.putIfAbsent(field.getName(),field.getName());             }        }        return map;    }    public static String resolveFieldName(String getMethodName) {        if (getMethodName.startsWith("get")) {            getMethodName = getMethodName.substring(3);        } else if (getMethodName.startsWith("is")) {            getMethodName = getMethodName.substring(2);        }        return firstToLowerCase(getMethodName);    }    public static String firstToLowerCase(String param) {        return param==null || "".equals(param.trim()) ? "" :                param.substring(0, 1).toLowerCase() + param.substring(1);    }}


3.记录列


3.枚举类

4.注解类

5.工具类
工具类的lambda解析的接口,我是指定了解析“继承”了Function这个接口,才会被解析,mybatis-plus外面是写死了 解析SFunction,这样限制性很大,而后脱离了mybatis-plus框架这个解析类的很多性能就用不了。

6.接口

package interfaces;import java.io.Serializable;import java.util.function.Function;/** * <ul> * <li>Title: SFunction</li> * <li>Description: TODO </li> * </ul> * * @author 程序员ken * @date 2021/4/28 0028 下午 14:33 */@FunctionalInterfacepublic interface SFunction<T, R> extends Function<T,R>, Serializable {}

7.测试:

8.其余

另外在own包下我也仿写了一个这样的串写lambda的示例,所有的测试案例在LambadaTest外面doun都能找到。

总结:其实本文也并没有深刻源码,只是让大抵理解这个框架的原理。

【纸上得来终觉浅,绝知此事要躬行】

(多看看优良的代码,这样你的代码才会有提高哦,不要做一个只会curd的boy哦)

源码地址:[https://gitee.com/ten-ken/myb...

欢送关注我的公众号:程序员ken,程序之路,让咱们一起摸索,共同进步。