前言
对于 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
*/
@FunctionalInterface
public interface SFunction<T, R> extends Function<T,R>, Serializable {}
7. 测试:
8. 其余
另外在 own 包下我也仿写了一个这样的串写 lambda 的示例,所有的测试案例在 LambadaTest 外面 doun 都能找到。
总结:其实本文也并没有深刻源码,只是让大抵理解这个框架的原理。
【纸上得来终觉浅,绝知此事要躬行】
(多看看优良的代码,这样你的代码才会有提高哦,不要做一个只会 curd 的 boy 哦)
源码地址:[https://gitee.com/ten-ken/myb…
欢送关注我的公众号:程序员 ken,程序之路,让咱们一起摸索,共同进步。