1. 目录

[TOC]

2. 说明

mybatis的请求流程分为带事务的请求和不带事务的请求,其在事务提交、关闭上略有不同,其余流程均差不多

StatementHandlerExecutionHandlerParaedmentHandler

3. 依赖分析

4. 源码分析

前面已经分析到实际注入的Mapper为MapperProxy代理对象,因此,我们的调用从MapperProxy开始

MapperProxy

由于实现了jdk动态代理,进入方法首先分析invoke()方法

1
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
27
28
29
30
31
32
33
34
35
public class MapperProxy<T> implements InvocationHandler, Serializable {

private static final long serialVersionUID = -6424540398559729838L;
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;
}

// 这里是动态代理执行的入口
//首先,它检查方法是否为Object类中的方法,如果是,则直接调用该方法。否则,它会检查方法是否为默认方法,并调用默认方法。如果都不是,则根据方法信息找到对应的MapperMethod对象,并执行该方法。
//这里的proxy是代理类,而不是原类,因此前面两个方法都不会走
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//不走
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
//不走
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//走这里,缓存一下再创建MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//这里进入mapperMethod.execute方法
return mapperMethod.execute(sqlSession, args);
}
}

MapperMethod在创建的时候加入了Configuration,使得其自身功能很强大

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class MapperMethod {

//增删改查分别走自己的逻辑
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;
}
case SELECT:
//查询这里的逻辑根据返回值的类型走不同的逻辑,主要是返回组装的问题,我们看查询selectList来分析
if (method.returnsVoid() && method.hasResultHandler()) {
//没有返回走这里
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//list走这里
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
//Map走这里
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
//这个一般都不怎么用,有游标等,有特殊逻辑
result = executeForCursor(sqlSession, args);
} else {
//查询单个走这里
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
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;
}
}

SELECT流程分析

对于增删改查,其逻辑大致相当,不同的是查询有缓存处理逻辑,增删改有缓存删除逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//MapperMethod
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
//这里转换参数,直接先不管,主要是有一些自定义的参数转换器,其余的就是普通的转化
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
//看这里sqlSession.selectList,这里面才是重点SqlSessionTemplate
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}

SqlSessionTemplate#selectList()

SqlSessionTemplate 主要构造代理的sqlsession,会增加SqlSessionInterceptor拦截处理器

构造器构造代理类

1
2
3
4
5
6
7

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}

SqlSessionTemplate 的执行器,主要执行代理的selectlist方法,

1
2
3
4
public <E> List<E> selectList(String statement, Object parameter) {
//进代理的sqlsession
return this.sqlSessionProxy.<E> selectList(statement, parameter);
}

代理类因为有多个执行方法如selectList,selectOne,但是其获取连接,访问数据库逻辑又是一致的,只有部分不一样,因此,构造一个拦截器统一执行代理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取SqlSession,从事务管理器中拿,没有则创建,创建是从sqlSessionFactory中开启创建的
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
//执行方法,即selectList/SelectOne方法等,这里执行进入DefaultSqlSession中,见下方
Object result = method.invoke(sqlSession, args);
//没有开事务的话执行完之后这里就该释放连接了,若是开了事务的话事务是交给Spring的事务管理器TransactionManagemnt的,这里就不会走
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory))
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
//......异常了之后关闭连接,并设置sqlsession为空,不管,
} finally {
if (sqlSession != null) {
//这里关连接分为事务和非事务,事务的把SqlSessionHolder中的指标减一,非事务就是真的执行关闭连接
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
DefaultSqlSession
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DefaultSqlSession implements SqlSession {
@Override
//执行这里
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//获取到MappedStatement,及Mapper的类中的唯一方法(因为mybatis不允许重载)
MappedStatement ms = configuration.getMappedStatement(statement);
//Execurotor在创建DefaultSqlSession的时候就从Configuration中创建了
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
Executor#query()

这里主要有CachingExecutor缓存执行器和真正的执行器,缓存执行器主要将查询的结果缓存起来,后续有则直接使用,

CachingExecutor 缓存执行器

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//缓存的执行器,实际上内部有个一delegate,是SimpleExecutor,主要逻辑在这里
public class CachingExecutor implements Executor {
private final Executor delegate;

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

//获取 BoundSql,这里面包含了sql相关的信息,也包括了Mapper和Xml的映射
BoundSql boundSql = ms.getBoundSql(parameterObject);

//获取缓存key,也需要从基础的Executor中获取
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
//执行查询操作
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}
//执行查询操作,
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
//有缓存的才缓存
// 二级缓存 https://blog.csdn.net/qq_35512802/article/details/128194303
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {

//查询
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

//缓存
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//或者走这里查询
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

SimpleExecutor 默认的执行器

1
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//...... 省略不重要的
try {
//栈+1
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//默认走这里从数据库查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
//......忽略deferredLoad相关
return list;
}
//从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//......忽略缓存相关
try {
//执行查询
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//......忽略缓存相关
}
//......忽略缓存相关
return list;
}

//执行查询流程
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//获取到StatementHandler , 这里获取到的是RoutingStatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//这里获取连接,预执行操作
stmt = prepareStatement(handler, ms.getStatementLog());
//这里执行jdbc查询操作PreparedStatement.execute(),并且组装返回,内部就先不看了,以后可以专门看组装返回的过程
return handler.<E>query(stmt, resultHandler);
} finally {
//关流
closeStatement(stmt);
}
}
//准备Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取jdbc连接
Connection connection = getConnection(statementLog);
//这里面主要是调用真实类PreparedStatementHandler,创建JdbcStatement,执行jdbc相关操作,,这里不继续分析了
stmt = handler.prepare(connection, transaction.getTimeout());
//主要是设置参数
handler.parameterize(stmt);
return stmt;
}

5. 总结

主要流程:

  • Proxy->MapperProxy 动态代理Mapper接口,其拦截器为MapperProxy,用来处理业务逻辑
  • MapperProxy将代理拦截器的Method构造MapperMethod(需要获取到Configuration,SqlsessionTemplate)
  • MapperMethod执行execute方法,根据方法的类型(CRUD)不同处理细微差异,然后调用Sqlsession对应CRUD方法
  • SqlSessionTemplate自行构造了一个动态代理用于处理公共请求,其根据传入的SqlSessionFactory创建一个DefaultSqlsession执行真正的CRUD,自身拦截器主要用于处理事务和提交、关闭sqlSession等
  • DefaultSqlsession包含Configuration和Executor,先从configuration中获取到MappedStatement,然后执行Executor的query/update方法
  • Executor默认使用CacheingExecutor(包含SimpleExecutor delegate),其主要处理Mybatis二级缓存(namespace级缓存)TransactionalCacheManager,处理之后调用真正处理的SimpleExecutor
  • SimpleExecutor用来处理Mybatis一级缓存(session缓存)、预处理(打开连接、设置参数)和执行StatementHandler
  • StatementHandler 真正的执行jdbc和调用ResultSetHandler组装返回结果

6. 注意事项

  • SqlSessionTemplate在没有事务的情况下每次请求都会创建一个DefaultSqlsession,在有事务的情况下,会创建DefaultSqlsession并缓存在spring-txTransactionSynchronizationManager中,由事务管理器销毁
  • 一级缓存和二级缓存可参考Mybatis源码解析(十):一级缓存和二级缓存