1. Mapper 代理层执行
Mapper 代理上执行方法调用时,调用被委派给 MapperProxy 来处理。
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// 接口里声明的方法,转换为 MapperMethod 来调用
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 与 Spring 集成时此处的 sqlSession 仍然 SqlSessionTemplate
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
MapperMethod 根据 mapperInterface.getName() + "." + method.getName()
从 Configuration 对象里找到对应的 MappedStatement
,从而得到要执行的 SQL 操作类型(insert/delete/update/select/flush),然后调用传入的 sqlSession 实例上的相应的方法。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
// 把参数转换为 SqlSession 能处理的格式
Object param = method.convertArgsToSqlCommandParam(args);
// 在 sqlSession 上执行并处理结果
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
...省略
如果上述方法传入的是 SqlSessionTemplate
,那么这些方法调用会被 SqlSessionInterceptor
拦截,加入与 Spring 事务管理机制协作的逻辑,具体可以看这篇文章MyBatis 事务管理,这里不再展开,最终会调用到 DefaultSqlSession
实例上的方法。
2. 会话层的执行过程
SqlSession 里声明的所有方法的第一个参数如果是 String statement
,则都是 mapperInterface.getName() + "." + method.getName()
,表示要调用的 SQL 语句的标识符。通过它从 configuration 找到 MappedStatement
。
会话层最主要的逻辑是进行参数的包装,获取对应的 MappedStatement
,然后调用持有的 Executor
的方法去执行。