Mybatis源码分析-Mybatis是如何执行sql的

使用mybatis作为dao层框架是目前较主流的一种方案,与spring框架整合下的实践一般是先编写mapper接口,再编写mapper的xml文件,最后在service层中调用mapper接口进行数据库层面操作。举个系统岗位实例的curd做例子:

  • mapper接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface SysPostMapper
{
/**
* 查询岗位数据集合
*
* @param post 岗位信息
* @return 岗位数据集合
*/
public List<SysPost> selectPostList(SysPost post);

/**
* 查询所有岗位
*
* @return 岗位列表
*/
public List<SysPost> selectPostAll();

}
  • mapper的xml文件
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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 命名空间使用mapper接口全路径 -->
<mapper namespace="com.ruoyi.system.mapper.SysPostMapper">

<resultMap type="SysPost" id="SysPostResult">
<id property="postId" column="post_id" />
<result property="postCode" column="post_code" />
<result property="postName" column="post_name" />
<result property="postSort" column="post_sort" />
<result property="status" column="status" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="remark" column="remark" />
</resultMap>
<cache></cache>
<sql id="selectPostVo">
select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark
from sys_post
</sql>
<!-- namespace + id 将组成mappedStatement的唯一标识符 -->
<select id="selectPostList" parameterType="SysPost" resultMap="SysPostResult">
<include refid="selectPostVo"/>
<where>
<if test="postCode != null and postCode != ''">
AND post_code like concat('%', #{postCode}, '%')
</if>
<if test="status != null and status != ''">
AND status = #{status}
</if>
<if test="postName != null and postName != ''">
AND post_name like concat('%', #{postName}, '%')
</if>
</where>
</select>

<select id="selectPostAll" resultMap="SysPostResult">
<include refid="selectPostVo"/>
</select>

</mapper>
  • service层调用
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
@Service
public class SysPostServiceImpl implements ISysPostService
{
@Autowired
private SysPostMapper postMapper;

@Autowired
private SysUserPostMapper userPostMapper;

/**
* 查询岗位信息集合
*
* @param post 岗位信息
* @return 岗位信息集合
*/
@Override
public List<SysPost> selectPostList(SysPost post)
{
return postMapper.selectPostList(post);
}

/**
* 查询所有岗位
*
* @return 岗位列表
*/
@Override
public List<SysPost> selectPostAll()
{
return postMapper.selectPostAll();
}
}

举例完毕,本文要探究的是为什么调用mapper接口就可以操作数据库?

1 mapper接口不是接口,是动态代理类

接口自然是无法直接调用,真正在调用的其实是增强了该接口的动态代理类。mybatis使用的动态代理是基于jdk自带的那种,即基于接口的。下面举个例子说明下:

  • mapper接口
1
2
3
public interface StudentMapper {
void study();
}
  • 增强器(即实现了InvocationHandler)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MapperProxy<T> implements InvocationHandler {

private final Class<T> mapperInterface;

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

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("虽然是个接口,没有任何实现类,我也能直接执行!");
System.out.println("我还能知道你正在调用的方法:" + method.getName());
return null;
}
}
  • demo演示
1
2
3
4
5
6
7
public class Demo {
public static void main(String[] args) {
MapperProxy<StudentMapper> mapperProxy = new MapperProxy<>(StudentMapper.class);
StudentMapper studentMapper = (StudentMapper) Proxy.newProxyInstance(mapperProxy.getClass().getClassLoader(), new Class[]{StudentMapper.class}, mapperProxy);
studentMapper.study();
}
}

studentMapper是个接口,也没有任何实现类,但也可直接调用,因为实际调用的是动态代理类,其具体逻辑定义在MapperProxyinvoke方法中。例子虽简单,但mybatis的做法其实也是类似的,下面一起探究。

首先从DefaultSqlSession类的getMapper方法切入,其实与spring整合的场景下一般感知不到这个步骤,因为mapper的动态代理类已经交给spring容器管理了。

1
2
3
4
5
// org.apache.ibatis.session.defaults.DefaultSqlSession
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
1
2
3
4
// org.apache.ibatis.session.Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// org.apache.ibatis.binding.MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 核心逻辑在此
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
1
2
3
4
5
6
7
8
9
10
// org.apache.ibatis.binding.MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
// MapperProxy实现了InvocationHandler接口
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 基于jdk自动的动态代理生成
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

看看MapperProxy的具体逻辑把

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
public class MapperProxy<T> implements InvocationHandler, Serializable {

private static final long serialVersionUID = -4724728412955527868L;
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
private static final Constructor<Lookup> lookupConstructor;
private static final Method privateLookupInMethod;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache;

public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@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 {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
// ...................省略......................
}

由上面的源码分析可知,mapper接口执行的实际逻辑定义在MapperProxy的invoke方法中

2 mapper接口执行查询类sql的过程

2.1 mapper接口的执行入口

上面说过mapper接口是个动态代理类,分析过增强的逻辑即可知道真正执行逻辑封装在类MapperMehod中,故从此类切入

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
62
63
64
// org.apache.ibatis.binding.MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// command这类信息的封装在前面的节点已处理,大体可理解为从mapper的xml文件解析所得
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;
}
// 以select类型的sql为例子
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);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
// ...............省略..............
}

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
// 判断是否有分页信息数据
// 实际使用的是SqlSessionTemplate处理
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.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;
}
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
// org.mybatis.spring.SqlSessionTemplateorg.mybatis.spring.SqlSessionTemplate
// 与spring整合的环境下使用这个组件,其被SqlSessionInterceptor增强
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.selectList(statement, parameter);
}
// -----增强的逻辑----------
// org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在此获取正在的sqlSession,后续详细分析
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
// 调用目标方法
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}

2.2 获取SqlSession

说明下获取sqlSession的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// org.mybatis.spring.SqlSessionUtils
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {

notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
// 从事务同步管理器中观察是否已经有本线程之前放过的sqlSession对象,有则直接返回
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}

LOGGER.debug(() -> "Creating a new SqlSession");
// 没有则新获取一个session对象
session = sessionFactory.openSession(executorType);
// 把session绑定到线程上,当然这需要看是否开启配置 TransactionSynchronization
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

return session;
}

TransactionSynchronizationManager这个对象是spring-tx包下的,与spring的事务相关,可存放很多线程相关的变量,值得关注,后期将mybatis和事务结合起来的时候再重点分析。

接下来重点分析下openSession的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
// 其实大部分情况都是调用openSessionFromDataSource此方法获取session
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
// 与spring结合的话则是SpringManagedTransactionFactory,具体可看mybatisAutoConfiguration的配置
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// SpringManagedTransaction
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// CachingExecutor 代理对象SimpleExecutor 封装变量transaction configuration
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

session中必然要有数据库的信息,transaction封装了dataSourceexecutor中封装Transaction,最终在DefaultSqlSession中封装executor,链式调用,最终就有此信息

看看获取Executor的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//org.apache.ibatis.session.Configuration
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 根据不同类型生成,一般都是 Simple那种,然后再用Caching套上
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 值得注意,executor是会被增强的,此为插件机制的原理
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

2.2.1 值得关注的插件增强

用过分页插件的都知道其原理是增强了executor的实现类,增强的插入点就在这里:

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
//org.apache.ibatis.plugin.InterceptorChain
//这个chain啊,其实myatis的configuration中addInterceptor的时候会往里头加插件
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
// 返回的target是个代理对象,经过遍历后就会,,,一层套一层.....
target = interceptor.plugin(target);
}
return target;
}
//org.apache.ibatis.plugin.Interceptor
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
// org.apache.ibatis.plugin.Plugin
// 最核心的逻辑在这里
// plugin类实现了InvocationHandler接口,很显然有要进行动态代理增强了
// 这里的target一般是simpleExecutor
public static Object wrap(Object target, Interceptor interceptor) {
// 一般inteceptor会在类开头写下这类注解元信息,可参考PageInterceptor
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
// 获取type实现的接口中在signatureMap中定义的,说直白点就是判断是否要拦截这个接口
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}

//-----------------------------------分隔符-------------------------------------------
// plugin既然实现了InvocationHandler接口,那就看看他的增强逻辑吧
//org.apache.ibatis.plugin.Plugin
// 成员变量
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
// 增强的逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 只有定义的方法才拦截
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}

总结:executor是有可能被插件增强过的。

上面这一大段都是如何获取sqlSession的逻辑,为啥这么复杂呢?因为与spring整合后有事务这种概念,同个事务使用相同的sqlSession,引入了spring-tx中的组件TransactionSynchronizationManager用于获取同个线程中先前存过的sqlSession(如果存在)。梳理下来有这么几点:

  • 获取组件:executor transaction sqlsession
  • 组件的依赖,executor需要transactiontransaction中封了datasource的信息,sqlSession需要executor
  • 其他的点:
    • 如果TransactionSynchronizationManagerSqlSession支持直接返回,没有得新获取
    • executor是可以被plugin增强的

2.3 sqlSession的查询逻辑

有了sqlSession总算可以查数据了,下面开始分析!

回到逻辑:org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke

通过反射的方式调用类org.apache.ibatis.session.defaults.DefaultSqlSessionselectList方法

1
2
3
4
5
6
7
8
9
10
11
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
// wrapCollection的逻辑比较简单,如果parameter是collection或array类型的则套一层返回ParamMap对象来返回
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

经过多个重载方法后最终进入executor.query(ms, wrapCollection(parameter), rowBounds, handler);

如果statement有使用cache则会先从cache取,即配置二级缓存,不过目前没遇到配cache的情况,故简化处理,具体逻辑在cachingExecutor的deltegate成员,即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
62
63
64
65
66
67
68
69
70

// org.apache.ibatis.executor.SimpleExecutor
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
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--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
// 从数据库中取
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
// 本地缓存先放个占位符
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 逻辑在这
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 将结果放入缓存中
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}

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,后面细讲
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 预编译statement对象
stmt = prepareStatement(handler, ms.getStatementLog());
// 查询获取结果
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

总结下获取数据的过程:一般都是通过simpleExecutor此组件获取数据,先从一级缓存取,没有则从数据库取,然后放入缓存中;

2.4 StatemnetHandler组件真干活

获取数据到封装数据有个很重要的组件StatementHandler,接下来重点说明:

2.4.1 获取StatemnetHandler过程

1
2
3
4
5
6
7
8
9
// org.apache.ibatis.session.Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 实际上的类型是RoutingStatementHandler,根据ms.getStatementType让其包裹一个delegate做真正的活,一般是PreparedStatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 一样的,statementHandler也可以被plugin增强,但很少这么搞,比如PageInterceptor就不增强statementHandler接口
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

2.4.2 基于StatementHandler获取prepareStatement

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
62
63
64
65
66
67
68
69
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取数据库连接
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
// org.apache.ibatis.executor.statement.PreparedStatementHandler
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 一般情况都是直接 connection.prepareStatement(sql);
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}

//org.apache.ibatis.scripting.defaults.DefaultParameterHandler
//从handler.parameterize(stmt); 最终将到此处的逻辑
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) {
//这里的parameterObject就是指xxxxMapper中接口被调用时上送的参数
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {// 是否有对应的类型处理器
value = parameterObject;
} else {
//这种情况指的是上送的若是个对象,一般都没有类型处理器嘛
// 此时就通过属性名获取对应对象的getxxx的返回值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 设置参数,最终底层执行jdbc的操作,如ps.setLong(i,value)
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}

总结下获取statement的过程:

  • 获取connection
  • 通过connection和sql获取prepareStatement
  • 将参数填充到statement中,参数已经在mappedStatemntbounedSqlParameterMappings中封好,依次遍历封装进去

2.4.3 使用StatementHandler获取数据

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// org.apache.ibatis.executor.statement.PreparedStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
// -----------------分割线--------------------
// org.apache.ibatis.executor.resultset.DefaultResultSetHandler
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

final List<Object> multipleResults = new ArrayList<>();

int resultSetCount = 0;
// 比较简单,调stmt.getResultSet(),然后包一层到ResultSetWrapper中
// 当然如果用到了druid这类的连接池会比较复杂,但终究用的是resultSet的接口标准
ResultSetWrapper rsw = getFirstResultSet(stmt);
// xml中的resultMap有值则封装到这里
// 一般只会有一个,不考虑多resultMap的情况
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//处理结果,结束后将结果封装到multipleResults对象中,具体分析在后面
handleResultSet(rsw, resultMap, multipleResults, null);
// 这种情况暂时不考虑
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 一般也不会有这种属性
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
// 比较简单,对一个对象的特殊处理,否则直接返回
return collapseSingleResultList(multipleResults);
}

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
// 简化分析,不考虑父mapping的情况
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
// 使用默认的,DefaultResultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
// 处理完将defaultResultHandler封装的结果搬到multipleResults对象中
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
// 只分析simple的ResultMap,不考虑嵌套那种的
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
// 此循环即遍历resultSet的入口,最终结果封装到DefaultResultHandler组件中
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
// 不考虑这种
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 将resultSet的一个结果封装到rowValue的java对象中,具体过程后面细说
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
// 逻辑比较简单,将rowValue放入DefaultResultHandler的list变量中,为啥套了好几层就搞不懂
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 根据resultMap中的type创建返回的java对象,一般通过反射方式
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 一对没映射到的属性应用属性自动映射方式填充对象的属性值
// 填充的方式后面详细展开
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 映射属性填充到rowValue中
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
// 可以配置是返回新对象还是返回null
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
// 返回值
return rowValue;
}

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>();
final List<Object> constructorArgs = new ArrayList<>();
// 获取返回对象,下面细说
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
// 遍历resultMapping,如果存在懒加载属性,则通过动态代理方式增强返回对象
// 不细展开,应该挺复杂的...
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
break;
}
}
}
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
// 存在resultType对应的typeHandler则最终会调用其getResult方法获取值返回
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
// 目前没见过用这种的
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
// 一般是这种,通过反射创建返回对象
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}


// -------------------分隔符-------------------
//--------------自动装配属性的逻辑-----------------
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
// 创建未被resultMap映射列的信息,逻辑也简单,遍历column找出没在resultmap中定义过的
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
// 对不存在resultMap的属性用typeHandler获取值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
// 填充映射的属性,即resultMap中有定义的
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
// 一般不会进入此逻辑
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERRED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
// 此逻辑较简单,metaObject中封装了目标返回对象rowValue,通过property名称寻找setProperty方法,通过方式方式将value值填充到rowValue中
metaObject.setValue(property, value);
}
}
}
return foundValues;
}

上面分析了使用preparedStatementHandler获取对象的具体过程,总结成下面几点:

  • 调用statement.execute,其实就是进行查库
  • 使用DefaultResultSetHandler的handleResultSets方法处理数据,其实就是遍历resultset,通过反射方式创建对象,设置对象值
  • 理一下几个组件:metaObject rowValue DefaultResultHandler的list变量 multipleResults,先创建对象作为rowValue,把rowValue封到metaObject中,根据resultSet封属性的时候操作的是metaObject,那么其实也封到了rowValue中,rowValue后面会封到DefaultResultHandler的list变量,从DefaultResultHandler的list变量赋值到multipleResults后返回

到此,获取数据的整个过程结束,后续操作一般是善后类的,如关闭sqlSession之类

3 mapper接口执行更新类sql的过程

前面介绍了查询语句的逻辑,下面介绍下更新语句的逻辑,有了上面的分析过程,看update就轻松很多。

还是从MapperProxy切入

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
// org.apache.ibatis.binding.MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
// .................省略
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
// 最终调用sqlSessionTemplate进行update操作
result = rowCountResult(sqlSession.update(command.getName(), param));
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;
}
// -----------------------分割--------------
// org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor
// sqlSession此对象被SqlSessionInterceptor增强,故先入此逻辑,基本都是这种操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}

mapperMethod最终也是调用DefaultSqlSessionupdate方法,具体分析下此方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// org.apache.ibatis.session.defaults.DefaultSqlSession
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
// caChingExecutor,但其实可能被plugin增强过
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// org.apache.ibatis.executor.SimpleExecutor
// cachingExecutor最终使用的也是SimpleExecutor组件
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 清空一级缓存
clearLocalCache();
return doUpdate(ms, parameter);
}

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 和查询操作一样,也是使用statementHandler,具体使用的是RoutingStatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}

获取prepareStatement的过程和查询是一样的,三步走:1获取连接connection,2基于connection生成prepareStatement,3使用statementHandler填充prepareStatement中的参数

下面具体分析statementHandlerupdate逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
// org.apache.ibatis.executor.statement.PreparedStatementHandler
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行jdbc的查询
ps.execute();
int rows = ps.getUpdateCount();
// 获取当初mapper中上送的参数
Object parameterObject = boundSql.getParameterObject();
// key生成器,update操作一般没配,则是 NoKeyGenerator,无逻辑的类
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}

更新操作的逻辑很简单,没有查询那么复杂。关于key生成器的使用虽然update操作没用到,但是insert操作时经常使用,具体表现就是如果主键是自增的,则插入完成后可将自增生成的主键自动设置到对象中,下面具体分析下其逻辑

3.1 关于KeyGenerator

keyGenerator是一个接口,mybatis提供了几个实现类:

  • Jdbc3KeyGenerator 自增主键那一类使用这种
  • SelectKeyGenerator oracle那类需要指定selectKey的使用这种
  • NoKeyGenerator 空操作

分析下Jdbc3KeyGenerator 的逻辑

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

// org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
processBatch(ms, stmt, parameter);
}
public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
// ms里已经封好了主键属性
final String[] keyProperties = ms.getKeyProperties();
if (keyProperties == null || keyProperties.length == 0) {
return;
}
try (ResultSet rs = stmt.getGeneratedKeys()) {
// 获取rs的元信息
final ResultSetMetaData rsmd = rs.getMetaData();
final Configuration configuration = ms.getConfiguration();
if (rsmd.getColumnCount() < keyProperties.length) {
// Error?
} else {
// 映射键,逻辑较复杂,不深究了,打住
assignKeys(configuration, rs, rsmd, keyProperties, parameter);
}
} catch (Exception e) {
throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
}
}

4 sql执行过程总结

画图总结

参考自 《一本小小的MyBatis源码分析书》

5 最后

本文从源码角度分析了mybatis执行sql的过程,可能有不严谨和理解不正确的地方,在以后有更多使用经验后不断修正。

参考文章:

MyBatis 源码分析系列文章合集