<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
首先写一个单测,然后跟随断点看
@Test
public void getMaxUpdateTimeByPositions(){
assert appBlockDao.getMaxUpdateTimeByPositions("mine")!=null;
}
跟着断点进来,发现进入了org.apache.ibatis.binding.MapperProxy动态代理类,执行invoke方法
//获取一个MapperMethod
MapperMethod mapperMethod = this.cachedMapperMethod(method);
//接着执行execute方法,传入一个sqlSession,与被代理方法的入参
return mapperMethod.execute(this.sqlSession, args);
/**
* cachedMapperMethod会从methodCache中去获取MapperMethod,获取不到则new一个
* 1、入参为当前被调用接口 private final Class<T> mapperInterface;
* 2、method
* 3、调用sqlSession.getConfiguration()接口,获取sqlSession中的的Configuration对象,由于
* sqlSession都是从sqlSessionFactory中获取的,所以实际上调用的是
* org.apache.ibatis.session.defaults.DefaultSqlSessionFactory的Configuration对象
*
*/
private MapperMethod cachedMapperMethod(Method method) {
//private final Map<Method, MapperMethod> methodCache;
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
初始化MapperMethod =>org.apache.ibatis.binding.MapperMethod下的SqlCommand
private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
//分别会初始化内部类SqlCommand、MethodSignature
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
//SqlCommand的实例化,篇幅有限,这里只暂时本次被代理接口执行的重要代码
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
//hasStatement方法主要是调用protected final Map<String, MappedStatement>
//mappedStatements,判断是否存在statementName,mappedStatements主要维护的是statementName与
//xml配置文件sql块的关系
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
}
//获取获取不到ms,会报这个熟悉的Invalid bound statement异常
if(ms==null){
if (method.getAnnotation(Flush.class) == null) {
throw new BindingException("Invalid bound statement (not found): " statementName);
}
}
...
//获取到ms后,给name复制未ms的id,这里的id=我们dao接口的全类名.方法名
this.name = ms.getId();
//SqlCommandType(type=SELECT)
this.type = ms.getSqlCommandType();
}
初始化MapperMethod =>org.apache.ibatis.binding.MapperMethod下的MethodSignature,主要是为MapperMethod中的一些参数赋值,为后续的流程使用,如returnType、入参params,判断是selectOne/List等
================================================================================================
到了这里,MapperMethod也就成功获取到了,开始执行execute部分,select请求会进入SqlCommandType.SELECT == this.command.getType()部分
mapperMethod.execute(this.sqlSession, args);
==================================================================================
public Object execute(SqlSession sqlSession, Object[] args) {
Object param;
Object result;
if (SqlCommandType.INSERT == this.command.getType()) {
...
} else if (SqlCommandType.UPDATE == this.command.getType()) {
...
} else if (SqlCommandType.DELETE == this.command.getType()) {
...
//获取刚才为SqlCommand中SqlCommandType赋值的type=SELECT
} else if (SqlCommandType.SELECT == this.command.getType()) {
if .//这里获取list、map等方法..{
else {
//param是一个map,存放了参数名与value
param = this.method.convertArgsToSqlCommandParam(args);
////由于我们的接口是返回Date类型,所以会执行SelectOne方法
result = sqlSession.selectOne(this.command.getName(), param);
}
} else {
...
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
sqlSession.selectOne首先经过=>
org.mybatis.spring.SqlSessionTemplate的内部类SqlSessionInterceptor的代理
通过SqlSessionUtils.getSqlSession获取SqlSession,获取过程=>
1、从Spring中TransactionSynchronizationManager中获取一个sqlSessionHolder
2、接着从sqlSessionHolder获取sqlSession,如果获取不到,那么就从SqlSessionFactory#openSession获取一个sqlSession,再调用SqlSessionUtils#registerSessionHolder注册到TransactionSynchronizationManager中
然后进入SqlSession的实际实现类:org.apache.ibatis.session.defaults.DefaultSqlSession,调用实际的selectList方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
//以statement为key,获取MappedStatement,与前面讲过的过程一致
MappedStatement ms = this.configuration.getMappedStatement(statement);
//执行CachingExecutor的query方法
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
========================================================================================
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//dao的sql语句信息,包括sql,parameterObject、metaParameters
BoundSql boundSql = ms.getBoundSql(parameterObject);
//获取一级缓存用的CacheKey,在BaseExecutor中创建、获取
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
然后会执行自身的另外一个query方法,首先判断MappedStatement中是否存在缓存Cache
如果不存在,那么会执行org.apache.ibatis.executor.BaseExecutor的query方法
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//内部使用一个ThreadLocal<ErrorContext>,设置resource对象等等
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
//判断是否从localCache中获取resultList
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//判断到为空后,就开始从数据库的查询
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator i$ = this.deferredLoads.iterator();
while(i$.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
queryFromDatabase,会调用BaseExecutor子类org.apache.ibatis.executor.SimpleExecutor中的doQuery方法
1、从Configuration中获取StatementHandler,调用Configuration#newStatementHandler方法,初始化org.apache.ibatis.executor.statement.RoutingStatementHandler,根据mapperStatement中的statementType判断需要返回SimpleStatementHandler/PreparedStatementHandler/CallableStatementHandler对象
2、接着执行org.apache.ibatis.executor.SimpleExecutor中的prepareStatement方法,在这里获取jdbc Connection、Statement等,从而建立数据库链接,然后调用StatementHandler的prepare方法,获取Statement,调用org.apache.ibatis.executor.statement.BaseStatementHandler#instantiateStatement()方法,调用Connection.prepareStatement方法,
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog);
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
3、然后调用org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize()方法,设置占位符参数PreparedStatement
4、调用ResultSetHandler#handleResultSets方法,然后结果集合ResultSet
5、org.apache.ibatis.executor.SimpleExecutor#closeStatement()方法,关闭org.apache.ibatis.executor.BaseExecutor#state方法,接着移除localCache,再把查询到的结果list和key再放入localCache中
动态代理=》SqlSession=>MapperStatement=》Executor=>Handler