Mybatis 实现类的执行过程-查询方法
# 110.Mybatis 实现类的执行过程-查询方法
我们从上一节的 DAO 的源码,一步步分析代码执行的过程
# 打断点
我们在 MybatisTestImpl
的 testFindAll
方法上打一个断点:
在 UserDaoImpl
类的 findAll
上也打一个:
然后我们 debug 运行:可以在 test 方法上右键--调试 xxx :
# SqlSession
调试的过程中,首先我们可以看到实现类分别是 DefaultSqlSessionFactory 和 DefaultSqlSession
知道这个后,我们先停止调试。
我们点进 SqlSession
的 源码,然后寻找它的实现类:在类名上右键--diagram--show diagram,例如:
可以看到这样的图:这两个接口都是 java 提供的,用来关闭资源的接口
我们回到 SqlSession,可以查看其实现类:
根据之前的 debug,我们选择 DefaultSqlSession:
然后我们就可以看到其源码了:
我们观察 selectList 方法:可以看到有 4 个重载
public <E> List<E> selectList(String statement) {
return this.selectList(statement, (Object)null);
}
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
List var6;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
this.dirty |= ms.isDirtySelect();
var6 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler);
} catch (Exception var10) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var10, var10);
} finally {
ErrorContext.instance().reset();
}
return var6;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
可以看到,前 3 个 selectList 方法,最终调用的都是第 4 个 selectList 方法;而该方法通过调用 executor.query
方法返回数据;我们点进 query 方法,可以看到这又是一个接口:
public interface Executor {
}
2
# Executor
为了看到具体是哪个实现类,我们打个断点:
通过断点调试,我们看到其调用的实现类是 CachingExecutor
我们找到有 4 个方法重载的 query 方法:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
2
3
4
5
注:我们可以在这里加个断点,然后继续调试,可以看到确实是执行了这个 query 方法:
可以看到 query 最后又调用了一个 6 个参数的 query 方法,而该 query 方法最后调用的是 delegate.query
方法
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//..........
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
2
3
4
可以看到最后 delegate,实现类是 SimpleExecutor
SimpleExecutor 类中,没有 query 方法,这是因为 BaseExecutor 里有 query 方法,该方法最终调用的就是 doQuery 方法,该方法是抽象方法,最终被 SimpleExecutor 实现:
public class SimpleExecutor extends BaseExecutor {
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
该 doquery 方法最终调用的是
handler.query(stmt, resultHandler)
而这个 Handler 是 RoutingStatementHandler
:
# Handler
我们通过查找 StatementHandler
的实现类 RoutingStatementHandler
,可以看到其还是有 query 方法:
而最后调用的就是 PreparedStatementHandler
的 query 方法
该 query 方法的内容如下:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return this.resultSetHandler.handleResultSets(ps);
}
2
3
4
5
这个 ps 是一个代理对象:execute 能执行任意的 SQL,我们在讲解 JDBC 时讲过了:JDBC 常用类介绍 (opens new window)
执行完后做了结果集的封装,ResultSetHandler
接口只有一个实现类: DefaultResultSetHandler
。handleResultSets 方法部分源码如下:
while(rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);
this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);
rsw = this.getNextResultSet(stmt);
this.cleanUpAfterHandlingResultSet();
++resultSetCount;
}
2
3
4
5
6
7
也就是获取每一行的数据,然后第 3 行那里调用 handleResultSet 处理每一行的数据,例如根据列名和 Java 类名进行映射;
# 梳理
我们在 MybatisTestImpl 里调用了 Dao 实现类的 session.selectList 方法:
public List<User> findAll() {
SqlSession session = factory.openSession();
List<User> users = session.selectList("com.peterjxl.dao.IUserDao.findAll");
session.close();
return users;
}
2
3
4
5
6
该 Session 是 DefaultSqlSession;由 DefaultSqlSessionFactory 工厂创建。
DefaultSqlSession 调用的 selectList 方法, 有很多个重载,但最终调用的是 Executor 的 query 方法
Executor 是个接口,实现类有 CachingExecutor,该类里也有多个重载的 query 方法,最终调用的是 delegate.query 方法;
delegate 是 SimpleExecutor 的实例,其 query 方法最后调用的是 RoutingStatementHandler 的方法;
RoutingStatementHandler 类里,就有一个 PreparedStatement 对象,并且调用 JDBC 的 execute 方法执行任何的 SQL,最后通过 resultSetHandler 封装返回的结果。
除了查询,删除和修改的执行过程我们就不一一演示了,也是类似的执行过程,并且其底层都是调用 JDBC 的接口
此外,断点调试和跟踪的方法,是我们在实际开发中经常用到的 ,希望读者们能好好掌握
# 代理对象
之前我们是自己实现的 DAO,调用了 Session 的 select 方法;而如果是代理对象呢?其内部是调用 selectList 方法的原理是什么呢?
我们根据之前的模式来,首先代理对象是通过 getMapper 方法获取的;
userDao = session.getMapper(IUserDao.class);
getMapper 是 session 接口的方法,我们得找其实现类 DefaultSqlSession 的源码,其 getMapper 方法如下:
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
2
3
Configuration
里方法如下:
public MapperRegistry getMapperRegistry() {
return this.mapperRegistry;
}
2
3
MapperRegistry
中,则创建了代理对象(第 7 行,熟悉的 newInstance)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
MapperProxyFactory
的源码:
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
2
3
4
5
6
7
8
也就是最后就是调用 Proxy 的 newProxyInstance 方法。因此我们的关注点,就是 mapperProxy,其内部应该是实现了增强方法的逻辑。
MapperProxy 部分源码:可以看到其实现了 InvocationHandler,并且有 invoke 方法:
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
}
2
3
4
5
6
7
8
9
10
观察 cachedInvoker,可以看到其用到了一个类 MapperMethod
:
MapperMethod
部分源码如下:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch (this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
//..... 省略其他
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
看到这里,大家应该都知道了,其最后就是根据 switch 判断执行什么 SQL,然后调用对应的方法,例如 sqlSession.insert,sqlSession.update。
而 insert,update 方法,最后的调用过程,跟我们本文前半部分讲的一样,也是 Executor 和 Handler。