在 mybatis源码分析_06_mybatis-plus源码分析 一文中,我们了解到了mybatis-plus的核心原理,知道了Wrapper构建动态SQL的过程,对于执行流程没有展开分析,虽然与mybatis很相似,但是仍然存在一些问题没有解决,比如:
本文将通过源码分析以上几个问题。
系列文章:
在BaseMapper接口里面:
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// String WRAPPER = "ew";
这个是selectList方法的声明,可以看到参数加了@Param注解,注解的value就是ew。
之前我们分析过mybatis-plus的getMapper(Class)方法,最终走到MybatisMapperRegistry的getMapper(Class)方法中:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 这里换成MybatisMapperProxyFactory而不是MapperProxyFactory
// 这行代码之前分析过了
final MybatisMapperProxyFactory<T> mapperProxyFactory = knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
}
try {
// 这里很重要,是在创建代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
mapperProxyFactory.newInstance(sqlSession)创建代理对象,MybatisMapperProxyFactory类:
public class MybatisMapperProxyFactory<T> {
@Getter
private final Class<T> mapperInterface;
@Getter
private final Map<Method, MybatisMapperProxy.MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MybatisMapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@SuppressWarnings("unchecked")
protected T newInstance(MybatisMapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(
mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MybatisMapperProxy<T> mapperProxy =
new MybatisMapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
这个类与mybatis的MapperProxyFactory类似,只是这个类使用的是mybatis-plus的MybatisMapperProxy作为代理InvocationHandler实现。
这类实现了InvocationHandler接口,InvocationHandler接口的invoke方法在代理对象拦截到目标方法时执行,看一下MybatisMapperProxy的invoke方法的实现:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 我们关注的是这个分支
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 首先从methodCache中获取目标method的MapperMethodInvoker的实现
// 如果没有就创建一个新的
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return CollectionUtils.computeIfAbsent(methodCache, method, m -> {
// 这个分支是判断default方法,不需要关注
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 这个创建一个PlainMethodInvoker实现对象
// 需要传递一个MybatisMapperMethod对象作为参数
// 这两个类都由mybatis-plus提供
return new PlainMethodInvoker(
new MybatisMapperMethod(
mapperInterface, method, sqlSession.getConfiguration()
)
);
}
});
} catch (RuntimeException re) {
// ...
}
}
PlainMethodInvoker类,看一下代码就行,不需要特别说明:
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MybatisMapperMethod mapperMethod;
public PlainMethodInvoker(MybatisMapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}
@Override
public Object invoke(
Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// 调用mapperMethod的execute方法
return mapperMethod.execute(sqlSession, args);
}
}
需要看一下MybatisMapperMethod类。
这个类与mybatis提供的MapperMethod类似,先看一下构造方法:
public class MybatisMapperMethod {
private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
public MybatisMapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// select、insert、update、delete等
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
// 这里比较重要
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
// ...
}
// 以下内容都是mybatis提供的了:
// MapperMethod.MethodSignature
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany =
configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 这里创建ParamNameResolver,用于解析方法参数
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
// ParamNameResolver
public ParamNameResolver(Configuration config, Method method) {
// 这个默认是true
this.useActualParamName = config.isUseActualParamName();
// 参数列表
final Class<?>[] paramTypes = method.getParameterTypes();
// 所有参数所有注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
// 参数个数
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 这个分支进不来
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
// 遍历参数的所有注解
for (Annotation annotation : paramAnnotations[paramIndex]) {
// 只解析@Param注解
if (annotation instanceof Param) {
hasParamAnnotation = true;
// 获取到Param名,selectList方法的话这里的值是ew
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
if (useActualParamName) {
// 这里解析成arg0、arg1 ...
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
// 得到一个Map结构
// index -> param name
// selectList方法的话,就是0->ew
names = Collections.unmodifiableSortedMap(map);
}
看一下execute方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 查询操作进入这个分支
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
// TODO 这里下面改了
if (IPage.class.isAssignableFrom(method.getReturnType())) {
result = executeForIPage(sqlSession, args);
// TODO 这里上面改了
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
// 抛异常
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
// 抛异常
}
return result;
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// MapperMethod.MethodSignature method
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
executeForMany方法之前在 mybatis源码分析_03_mapper接口源码分析 一文中做过分析,不过当时没有详细分析convertArgsToSqlCommandParam的流程,此处需要补充一下这部分内容,因为这与mybatis-plus的核心查询流程关系密切。
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
// paramNameResolver.getNamedParams(args)
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
// hasParamAnnotation = true所以这个分支进不来
Object value = args[names.firstKey()];
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
} else {
// 用于封装参数,例如ew -> LambdaQueryWrapper参数对象
// 或者param1 -> LambdaQueryWrapper参数对象
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// entry: 0 -> ew
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
所以到这里我们就可以知道ew就是selectList方法的参数,也就是LambdaQueryWrapper参数对象。
我们之前看过格式化后的动态SQL:
<script>
<choose>
<when test="ew != null and ew.sqlFirst != null">
${ew.sqlFirst}
</when>
<otherwise></otherwise>
</choose> SELECT
<!-- 拼接查询字段 -->
<choose>
<when test="ew != null and ew.sqlSelect != null">
${ew.sqlSelect}
</when>
<otherwise>id,title,content,create_time,update_time</otherwise>
</choose> FROM blog <!-- 拼接表名 -->
<!-- 拼接查询条件 -->
<if test="ew != null">
<where>
<!-- 拼接实体对象查询条件 -->
<if test="ew.entity != null">
<if test="ew.entity.id != null">id=#{ew.entity.id}</if>
<if test="ew.entity['title'] != null"> AND title=#{ew.entity.title}</if>
<if test="ew.entity['content'] != null"> AND content=#{ew.entity.content}</if>
<if test="ew.entity['createTime'] != null"> AND create_time=#{ew.entity.createTime}</if>
<if test="ew.entity['updateTime'] != null"> AND update_time=#{ew.entity.updateTime}</if>
</if>
<!-- 拼接Wrapper对象查询条件 -->
<if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfWhere">
<if test="ew.nonEmptyOfEntity and ew.nonEmptyOfNormal"> AND</if> ${ew.sqlSegment}
</if>
</where>
<!-- 拼接Wrapper对象查询条件,这里通常没有办法进来,存在疑问 -->
<if test="ew.sqlSegment != null and ew.sqlSegment != '' and ew.emptyOfWhere">
${ew.sqlSegment}
</if>
</if>
<choose>
<when test="ew != null and ew.sqlComment != null">
${ew.sqlComment}
</when>
<otherwise></otherwise>
</choose>
script>
在前面的分析中,可以知道ew就是方法的参数,也就是LambdaQueryWrapper参数对象。
这个属性定义在AbstractWrapper类中,用于获取实体类查询条件:
private T entity;
public T getEntity() {
return entity;
}
用于为以下动态SQL赋值:
<if test="ew.entity != null">
<if test="ew.entity.id != null">id=#{ew.entity.id}if>
<if test="ew.entity['title'] != null"> AND title=#{ew.entity.title}if>
<if test="ew.entity['content'] != null"> AND content=#{ew.entity.content}if>
<if test="ew.entity['createTime'] != null"> AND create_time=#{ew.entity.createTime}if>
<if test="ew.entity['updateTime'] != null"> AND update_time=#{ew.entity.updateTime}if>
if>
这个属性定义在ISqlSegment接口中,用于获取动态查询SQL代码片段:
@FunctionalInterface
public interface ISqlSegment extends Serializable {
String getSqlSegment();
}
他有几个重要的实现类:
在之前分析LambdaQueryWrapper的时候介绍过MergeSegments类,在AbstractWrapper中使用他封装所有的动态条件表达式,也看过add动态条件表达式的代码。
AbstractWrapper.getSqlSegment()方法:
public String getSqlSegment() {
return expression.getSqlSegment() + lastSql.getStringValue();
}
// 这个是expression是MergeSegments对象,用于聚合了其他类型的ISqlSegment对象
MergeSegments.getSqlSegment()方法:
public String getSqlSegment() {
if (cacheSqlSegment) {
return sqlSegment;
}
cacheSqlSegment = true;
if (normal.isEmpty()) {
if (!groupBy.isEmpty() || !orderBy.isEmpty()) {
sqlSegment = groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();
}
} else {
sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();
}
return sqlSegment;
}
// normal.getSqlSegment()
public String getSqlSegment() {
if (cacheSqlSegment) {
return sqlSegment;
}
cacheSqlSegment = true;
sqlSegment = childrenSqlSegment();
return sqlSegment;
}
// NormalSegmentList.childrenSqlSegment()
protected String childrenSqlSegment() {
if (MatchSegment.AND_OR.match(lastValue)) {
removeAndFlushLast();
}
// 这里拼接动态查询SQL代码片段
final String str = this.stream().map(ISqlSegment::getSqlSegment).collect(Collectors.joining(SPACE));
return (LEFT_BRACKET + str + RIGHT_BRACKET);
}
SqlKeyword.getSqlSegment()方法:
public enum SqlKeyword implements ISqlSegment {
AND("AND"), OR("OR"), NOT("NOT"), IN("IN"), NOT_IN("NOT IN"),
LIKE("LIKE"), NOT_LIKE("NOT LIKE"), EQ(StringPool.EQUALS),
NE("<>"), GT(StringPool.RIGHT_CHEV), GE(">="), LT(StringPool.LEFT_CHEV),
LE("<="), IS_NULL("IS NULL"), IS_NOT_NULL("IS NOT NULL"), GROUP_BY("GROUP BY"),
HAVING("HAVING"), ORDER_BY("ORDER BY"), EXISTS("EXISTS"), NOT_EXISTS("NOT EXISTS"),
BETWEEN("BETWEEN"), NOT_BETWEEN("NOT BETWEEN"), ASC("ASC"), DESC("DESC");
private final String keyword;
@Override
public String getSqlSegment() {
return this.keyword;
}
}
这个属性定义在Wrapper类中,用于判断查询条件不为空:
public boolean nonEmptyOfWhere() {
return !isEmptyOfWhere();
}
public boolean isEmptyOfWhere() {
return isEmptyOfNormal() && isEmptyOfEntity();
}
这个属性定义在Wrapper类中,用于深层实体判断属性:
public boolean isEmptyOfEntity() {
return !nonEmptyOfEntity();
}
public boolean nonEmptyOfEntity() {
T entity = getEntity();
if (entity == null) {
return false;
}
TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
if (tableInfo == null) {
return false;
}
if (tableInfo.getFieldList().stream().anyMatch(e -> fieldStrategyMatch(entity, e))) {
return true;
}
return StringUtils.isNotBlank(tableInfo.getKeyProperty()) ?
Objects.nonNull(ReflectionKit.getFieldValue(entity, tableInfo.getKeyProperty())) : false;
}
这个属性定义在Wrapper类中,用于判断查询条件不为空:
public boolean nonEmptyOfNormal() {
return !isEmptyOfNormal();
}
public boolean isEmptyOfNormal() {
return CollectionUtils.isEmpty(getExpression().getNormal());
}
这个属性定义在Wrapper类中,用于判断where条件不为空:
public boolean isEmptyOfWhere() {
return isEmptyOfNormal() && isEmptyOfEntity();
}