查询逻辑源码阅读
2024/4/15...大约 4 分钟
查询逻辑源码阅读
数据库用了一个Json字段,但查询出来时null。不知道为什么,翻翻源码在哪里没有赋值上。
代码逻辑简述
接口查询部分逻辑很简单,直接调用Mapper接口中的方法。
Mapper接口逻辑
对应的SQL,使用resultType直接映射到VO对象
<select id="queryPageList" resultType="com.aps.core.domain.vo.ApsPlanProductTaskDailyVO">
SELECT
*
FROM
aps_plan_product_task_daily apptd
WHERE
apptd.STATUS = 1
ORDER BY apptd.daily_time DESC, apptd.create_time ASC, apptd.id ASC
</select>
VO对象:
@Data
@EqualsAndHashCode(callSuper = true)
public class ApsPlanProductTaskDailyVO extends ApsPlanProductTaskDaily implements Serializable {
// ...一些扩展信息
}
@Data
@EqualsAndHashCode(callSuper = true)
@TableName(value = "aps_plan_product_task_daily", autoResultMap = true)
public class ApsPlanProductTaskDaily extends BaseEntity {
// 映射表结构对象,其余字段省略...
@TableField(typeHandler = FastjsonTypeHandler.class)
private Map<String, Object> reqMsg;
}
跟踪代码
首先进入 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 {
// 走到这里,从缓存获取调用者。执行invoke方法
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// 第一次进入没有缓存,需要走下面逻辑
return CollectionUtils.computeIfAbsent(methodCache, method, m -> {
// m 为经过回调传过来的method。具有主体的非静态方法,在接口类型中声明
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 {
// 走到这。构造一个普通调用方法。记录mapper接口实现类、调用方法、sql会话的配置
return new PlainMethodInvoker(new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
看下cachedInvoker(method)
MybatisMapperMethod 类用来记录和执行Mapper方法
类里只暴露了创建和execute2个方法,这个execute就是invoke调用的方法 构造方法,给command、method赋值。调用ibatis里的MapperMethod创建command和method
public MybatisMapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// SQL命令
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
// SQL方法
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
// ibatis里的MapperMethod创建command
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
// 获取查询sql的mapper里的方法名
final String methodName = method.getName();
// mapper的接口类
final Class<?> declaringClass = method.getDeclaringClass();
// 解析映射语句,取到mappedStatements
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// name和type看下面图
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
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);
// 参数名称解析
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
看下invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// 调用上面那个公开方法
return mapperMethod.execute(sqlSession, args);
}
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;
}
// 我这里走到select
case SELECT:
// 返回空
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 返回Map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 返回游标
result = executeForCursor(sqlSession, args);
} else {
// 本次查询上面都没走,走到这。这是plus对Mybatis的改造,我这里正是要返回Ipage
// 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:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
private <E> Object executeForIPage(SqlSession sqlSession, Object[] args) {
IPage<E> result = null;
for (Object arg : args) {
// 有一个参数是分页信息,直接把这个对象当做响应对象使用
if (arg instanceof IPage) {
result = (IPage<E>) arg;
break;
}
}
Assert.notNull(result, "can't found IPage for args!");
// 把入参转换成sql命令参数,返回的使用map,映射了参数名和值以及对应param顺序和值,效果如下图
Object param = method.convertArgsToSqlCommandParam(args);
List<E> list = sqlSession.selectList(command.getName(), param);
result.setRecords(list);
return result;
}
SqlSessionTemplate->selectList方法
可以看到在这里就已经null了
咦,好像没有看到核心代码。SqlSessionTemplate->selectList方法,一下就过去了。
SqlSessionTemplate->selectList方法
会invoke到这个方法
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}