Mybatis执行Sql流程源码分析

环境:

1
mybatis-spring-boot-starter: 1.3.4

MybatisAutoConfiguration类中查看Mybatis的SqlSessionFactory接口的实现DefaultSqlSessionFactory是如何注册到Spring容器的。

由于不是本文重点,这里就不贴源码了,描述下大概流程:

Mybatis是通过SqlSessionFactoryBean工厂bean的方式获取bean实例的。

首先通过SqlSessionFactoryBean类的buildSqlSessionFactory方法中调用xmlMapperBuilder.parse()方法
Mapper接口的Class类型作为key,MapperProxyFactory实例作为value注册到MapperRegistry类上。

然后将MapperRegistry实例设置到Configuration类的mapperRegistry属性上。

最后调用SqlSessionFactoryBuilder类的build方法完成SqlSessionFactory实例的创建:

1
2
3
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

这里看下MapperProxyFactory类的源码:

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
public class MapperProxyFactory<T> {
// Mapper接口的Class类型的引用
private final Class<T> mapperInterface;
// Mapper接口中定义的方法
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}

public Class<T> getMapperInterface() {
return mapperInterface;
}

public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

}

可以看到通过调用MapperProxyFactorynewInstance方法完成Mapper接口代理的生成。这里用到了Jdk的动态代理。

这里说明一点,Mybatis和Spring整合使用SqlSession的实现是SqlSessionTemplate,它是线程安全的由Spring管理的单例bean。

当调用Mapper接口的方法时,实际上会调用MapperProxy代理的invoke方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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);
}
// 缓存Mapper接口的方法
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}

然后返回调用MapperMethod类的execute方法:

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
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:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
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;
}

可以看到这里通过命令模式处理对应的Sql。这里笔者选择执行一条查询命令,那么最终会调用该类的executeForMany方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 根据命名规则获取参数映射对象ParamMap
Object param = method.convertArgsToSqlCommandParam(args);
// 判断方法上是否有RowBounds参数,如果有则Sql语句加上 offset,limit
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
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;
}

接下来进入SqlSessionTemplateselectList方法:

1
2
3
4
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.<E> selectList(statement, parameter);
}

可以看到其实是调用的SqlSession的代理来调用实际的selectList方法。
查看SqlSessionTemplate类的构造方法的源码有这一行:

1
2
3
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());

可知SqlSessionTemplate持有SqlSession接口的代理,其默认实现是DefaultSqlSession。查看该类的selectList方法实现:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// statement是Mapper接口的方法的完全限定名,这里获取对应的映射信息
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}

由于Mybatis默认是打开本地缓存的,即每个Session都持有Executor的引用实现CachingExecutor
接下来进入CachingExecutorquery方法:

1
2
3
4
5
6
7
8
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取绑定的sql对象
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建缓存key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

进入BaseExecutor的query方法实现,有这么一行:

1
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

继续查看queryFromDatabase方法实现,有这么一行:

1
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

由于默认使用Mybatis的ExecutorType是SIMPLE,所以进入SimpleExecutor的doQuery方法实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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();
// 1. 根据映射语句执行插件来拦截的方法调用
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 2. 预编译语句
stmt = prepareStatement(handler, ms.getStatementLog());
// 3. 执行查询
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

第一点,查看实现会返回RoutingStatementHandler实例,由于默认的语句类型是StatementType.PREPARED
所以该类持有PreparedStatementHandler对象的引用。

第二点,prepareStatement方法实现:

1
2
3
4
5
6
7
8
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
// 委托调用PreparedStatementHandler的parameterize方法进行参数设置
handler.parameterize(stmt);
return stmt;
}

PreparedStatementHandler的parameterize方法会调用DefaultParameterHandlersetParameters方法处理参数映射:

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
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 获取参数映射列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 遍历参数映射列表
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 获取属性名
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// 根据属性名获取属性值
value = metaObject.getValue(propertyName);
}
// 获取参数类型处理器,假定参数是int/integer类型那么最终typeHandler实例是IntegerTypeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 获取参数Jdbc类型
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 设置参数,如果参数不为空最终会调用IntegerTypeHandler的setNonNullParameter方法设置对应参数值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}

第三点,委托调用PreparedStatementHandler的query方法:

1
2
3
4
5
6
7
8
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行语句
ps.execute();
// 处理结果并返回,唯一实现是DefaultResultSetHandler
return resultSetHandler.<E> handleResultSets(ps);
}

这里重点看下结果的映射,结果的类型处理和参数的类型处理都是基于TypeHandler接口的实现,可以看看其抽象类的实现BaseTypeHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result;
try {
result = getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
if (rs.wasNull()) {
return null;
} else {
return result;
}
}

这里getNullableResult方法是抽象方法,将由具体的子类实现。比如有个实体的属性是

1
private LocalDateTime createAt;

那么将会使用LocalDateTimeTypeHandler类的实现,也可以指定自己的TypeHandler实现,在<resultMap>标签
的子标签<result>自定义typeHandler:

1
<result property="createAt" column="create_at" typeHandler="org.apache.ibatis.type.LocalDateTimeTypeHandler"/>

到此,Mybatis的调用流程源码分析结束。

以上,如有问题欢迎提出!

如果对您有所帮助,欢迎投食!
0%