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。