在对数据库操作时,免不了要用到JDBC,但是这部分代码写起来非常的冗长繁琐。于是乎,“懒惰”的人们为了避免做重复相同的事情,持久层框架应运而生。Mybatis便是其中一款优秀的框架,使用非常广泛,免除了我们几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。我们不探讨Mybatis的基本使用,因为从它的官方文档就能获取。本文将从Mybatis Plugin为切入点,从而大致了解一下Mybatis的工作原理。不过一个完整的框架,不是一篇文章就能讲完的,因此本文也只是做到“抛砖引玉”作用,希望能梳理出一个大致的脉络,让大家在此基础上能更清晰地阅读Mybatis源码。
Mybatis Plugin介绍
Plugin即插件,这里我们可以理解成拦截器。这是Mybatis提供的开放功能,它允许我们对SQL执行过程中的某一些点进行拦截调用。因此我们把插件也可以理解成一种扩展,官方文档中提到四个核心接口:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
接口后面将能够拦截的方法全部列举了出来,这里并不直接代入性地介绍它们各自的作用,首先我们看下官方给的使用插件的例子:
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
public Object plugin(Object target) {
return Plugin.wrap(target, this);
public void setProperties(Properties properties) {
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
// 在配置文件中配置上就行
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
// 在配置文件中配置上就行
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
使用起来还是很简单的,只需要实现Interceptor 接口,然后用注解@Intercepts为它打上需要拦截的方法、类名的标记,然后配置文件丽注册上它即可。
Plugin注册
既然我们声明了一个自定义的插件类,这个类实现了Interceptor 接口,那总该框架要将它应用进去。肯定有一个地方将它注册进去了。
private void pluginElement(XNode parent) throws Exception {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
// 将解析出来的Interceptor放入到configruation中
configuration.addInterceptor(interceptorInstance);
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
// 将解析出来的Interceptor放入到configruation中
configuration.addInterceptor(interceptorInstance);
}
}
}
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
// 将解析出来的Interceptor放入到configruation中
configuration.addInterceptor(interceptorInstance);
}
}
}
我们看到,这里将配置文件中的插件节点解析出来放入到configuration配置中。
// Configuration的添加其实就是将插件放入插件链中。
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
// Configuration的添加其实就是将插件放入插件链中。
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
// Configuration的添加其实就是将插件放入插件链中。
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
从它取得名字interceptorChain,我们大致能猜出来插件的链式调用:
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
// 装载插件
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
// 装载插件
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
接下来,我们通过查找调用关系,看下pluginAll函数在哪里被调用:
public class Configuration {
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
// 该方法会被SqlSessionFactory调用,用来构造SqlSession对象。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
executor = new SimpleExecutor(this, transaction);
// 这里缓存默认开启,也就是常说的二级缓存的代理执行器,这个是二级缓存的全局开关,二级缓存也是依赖该CacheExecutor的实现。
executor = new CachingExecutor(executor);
executor = (Executor) interceptorChain.pluginAll(executor);
public class Configuration {
......
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 装载ParameterHandler插件
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 装载ResultSetHandler插件
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 装载StatementHandler插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
// 该方法会被SqlSessionFactory调用,用来构造SqlSession对象。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
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);
}
// 这里缓存默认开启,也就是常说的二级缓存的代理执行器,这个是二级缓存的全局开关,二级缓存也是依赖该CacheExecutor的实现。
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 装载Executor插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
......
}
public class Configuration {
......
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 装载ParameterHandler插件
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 装载ResultSetHandler插件
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 装载StatementHandler插件
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
// 该方法会被SqlSessionFactory调用,用来构造SqlSession对象。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
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);
}
// 这里缓存默认开启,也就是常说的二级缓存的代理执行器,这个是二级缓存的全局开关,二级缓存也是依赖该CacheExecutor的实现。
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 装载Executor插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
......
}
在创建上述解释的几种核心类型时候,开始对插件进行了装载。代码还是比较简单的,因此解析.xml配置文件时候对插件进行解析并保存到InterceptorChain中,然后对各个核心接口进行对象创建时,对插件进行装载注入。接下来就是再看下对插件注入的关键代码 Plugin.wrap(target, this):
public class Plugin implements InvocationHandler {
public static Object wrap(Object target, Interceptor interceptor) {
// 通过扫描自定义插件类上的注解@Intercepts,@Signature来获取需要代理的核心接口及其相应的方法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
new Plugin(target, interceptor, signatureMap));
public class Plugin implements InvocationHandler {
......
public static Object wrap(Object target, Interceptor interceptor) {
// 通过扫描自定义插件类上的注解@Intercepts,@Signature来获取需要代理的核心接口及其相应的方法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 动态代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
......
}
public class Plugin implements InvocationHandler {
......
public static Object wrap(Object target, Interceptor interceptor) {
// 通过扫描自定义插件类上的注解@Intercepts,@Signature来获取需要代理的核心接口及其相应的方法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 动态代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
......
}
熟悉动态代理的同学应该是不陌生的,因此configuration中,创建各个核心接口的方法中其实返回的是一个代理对象,其中已经装载了我们自定义的插件,只等待一个合适机会去触发!接下来我们就可以对还没有介绍的四个核心接口—Executor,ParameterHandler,ResultSetHandler,StatementHandler该出场了。
Executor-执行器
我们都知道,SqlSession是Mybatis对外暴露提供的一个接口,调用它来进行数据库的相关操作。它有一个默认实现DefaultSqlSession,比如当我们查询数据时,实际上它是将请求转交给Executor处理:
public class DefaultSqlSession{
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
ErrorContext.instance().reset();
public class DefaultSqlSession{
......
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根据id找出配置中对应的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();
}
}
......
}
public class DefaultSqlSession{
......
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根据id找出配置中对应的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();
}
}
......
}
其实在executor执行器中也定义了很多基本的数据库操作方法。因此基本上来说,SqlSession其实把相应的请求转交给Executor执行。我们对Executor中想要拦截的方法,其实就是在这里进行执行的,应该来说这一步是整个请求过程中比较开始一步。接下来就简单介绍下它里面的方法:
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查询。还有一个query返回的是游标,此处暂不列出来
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
Transaction getTransaction();
void close(boolean forceRollback);
// 更新操作
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查询。还有一个query返回的是游标,此处暂不列出来
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
//批量执行SQL时调用
List<BatchResult> flushStatements() throws SQLException;
//提交事务
void commit(boolean required) throws SQLException;
//回滚事务
void rollback(boolean required) throws SQLException;
//获取事务对象
Transaction getTransaction();
//关闭Executor
void close(boolean forceRollback);
//判断当前Executor是否关闭
boolean isClosed();
// 更新操作
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查询。还有一个query返回的是游标,此处暂不列出来
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
//批量执行SQL时调用
List<BatchResult> flushStatements() throws SQLException;
//提交事务
void commit(boolean required) throws SQLException;
//回滚事务
void rollback(boolean required) throws SQLException;
//获取事务对象
Transaction getTransaction();
//关闭Executor
void close(boolean forceRollback);
//判断当前Executor是否关闭
boolean isClosed();
我们可以根据需要对上述的方法进行拦截,定制出属于自己的功能!而对于Executor,MyBatis本身就提供一个公共抽象类来实现一些公共的方法:BaseExecutor。我们还是以查询入手,继续往下探究一下。
public abstract class BaseExecutor{
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建缓存键。以便相同的查询不再需要查库。此处即一级缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
@SuppressWarnings("unchecked")
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());
throw new ExecutorException("Executor was closed.");
if (queryStack == 0 && ms.isFlushCacheRequired()) {
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
// 这个用于延迟加载。嵌套查询情况下,有可能内部查询结果并没有处理完成,此时可以先加载外部查询结果,对嵌套查询延迟处理。
for (DeferredLoad deferredLoad : deferredLoads) {
// 一级缓存默认是SESSION级别,如果设置的是STATMENT级别,则清除缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
public abstract class BaseExecutor{
......
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建缓存键。以便相同的查询不再需要查库。此处即一级缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
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();
// 一级缓存默认是SESSION级别,如果设置的是STATMENT级别,则清除缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
......
}
public abstract class BaseExecutor{
......
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建缓存键。以便相同的查询不再需要查库。此处即一级缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
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();
// 一级缓存默认是SESSION级别,如果设置的是STATMENT级别,则清除缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
......
}
从上面的代码中,我们可以很清楚看到localCache其实就是一个缓存,它内部其实就是利用HashMap来实现的。这个缓存其实就是一级缓存,它将整个SqlSession查询的结果进行短暂的缓存,该缓存会在update或者事务提交、回滚时进行清除。
我们再看下BaseExecutor中queryFromDatabase的简单实现:
public abstract class BaseExecutor{
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
localCache.putObject(key, EXECUTION_PLACEHOLDER);
// doQuery是一个抽象方法,它由子类实现。
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
localCache.removeObject(key);
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
// 子类SimpleExecutor的doQuery方法。
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
// 上述已经分析的创建statmentHandler, 并将插件内容已经注入进来。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
public abstract class BaseExecutor{
.......
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 {
// doQuery是一个抽象方法,它由子类实现。
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;
}
......
}
// 子类SimpleExecutor的doQuery方法。
@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();
// 上述已经分析的创建statmentHandler, 并将插件内容已经注入进来。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
public abstract class BaseExecutor{
.......
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 {
// doQuery是一个抽象方法,它由子类实现。
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;
}
......
}
// 子类SimpleExecutor的doQuery方法。
@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();
// 上述已经分析的创建statmentHandler, 并将插件内容已经注入进来。
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
从数据库中查询还是比较简单的,核心的还是doQuery方法。最终doQuery由各自的子类实现,上面列出来的doQuery的实现,我选用的是默认的SimpleExecutor。从代码中看出来,executor又将查询交给StatementHandler执行。到这里发现又有一个核心接口暴露了出来:StatementHandler。
StatmentHandler–Statment处理器
从这个接口的名称我们也能看出,该处理器处理的对象已经开始围绕statment展开,这个就是平时我们写JDBC代码时绕不开的接口,在这里我们看到了它的身影,首先我们把该处理器接口方法简单列举一下:
public interface StatementHandler {
// 从connection中获取statment对象
Statement prepare(Connection connection, Integer transactionTimeout)
void parameterize(Statement statement)
void batch(Statement statement)
// 以下就是query delete update相关操作
int update(Statement statement)
<E> List<E> query(Statement statement, ResultHandler resultHandler)
<E> Cursor<E> queryCursor(Statement statement)
// ParameterHandler 参数处理器,该接口后续会提及
ParameterHandler getParameterHandler();
public interface StatementHandler {
// 从connection中获取statment对象
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 绑定参数
void parameterize(Statement statement)
throws SQLException;
// 批量执行SQL
void batch(Statement statement)
throws SQLException;
// 以下就是query delete update相关操作
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
// 相关的sql对象,sql中可能包含 “?”
BoundSql getBoundSql();
// ParameterHandler 参数处理器,该接口后续会提及
ParameterHandler getParameterHandler();
}
public interface StatementHandler {
// 从connection中获取statment对象
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 绑定参数
void parameterize(Statement statement)
throws SQLException;
// 批量执行SQL
void batch(Statement statement)
throws SQLException;
// 以下就是query delete update相关操作
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
// 相关的sql对象,sql中可能包含 “?”
BoundSql getBoundSql();
// ParameterHandler 参数处理器,该接口后续会提及
ParameterHandler getParameterHandler();
}
Mybatis默认使用的是RoutingStatementHandler,它实际上是一种策略模式方法,根据MappedStatement的类型来选择具体使用哪种StatmentHandler,其中包括SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler,它们分别对应JDBC中的Statment、PreparedStatement和CallableStatement。从插件角度来看,我们可以通过拦截该StatmentHandler接口, 来对statment做一些自定义处理。
我们简单看下SimpleStatmentHandler的query方法实现:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
return resultSetHandler.<E>handleResultSets(statement);
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
// 结果集映射处理
return resultSetHandler.<E>handleResultSets(statement);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
// 结果集映射处理
return resultSetHandler.<E>handleResultSets(statement);
}
上面其实就是调用statment来执行查询请求,接着又碰到了一个核心接口resultSetHandler,用来处理映射结果。因此从这里开始,请求才被发送出去。上面我们提及了下,boundSql包含了需要执行的SQL,但是有可能包含”?”,所以在PreparedStatementHandler中,需要对参数进行处理:
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
此时我们又看到了一个核心接口parameterHandler。parameterize方法会在执行之前,对SQL中绑定的参数进行处理。
ParameterHandler–参数处理器
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
该接口就比较简单,主要就是两个方法对参数处理。如果我们通过插件来拦截这个接口,我们可以对整个参数处理进行自定义的处理。它有一个默认实现DefaultParameterHandler。其中对参数的转换,又涉及到TypeHandler,ParameterMapping一些关键类,有兴趣同学可以自己阅读相关源码。
到这里我们可以稍微总结下,对于Executor,Parameterhandler的拦截,此时我们的查询请求依然在预处理的时机,statmentHandler则是真正请求发出的时机,接下来出场的ResultSetHandler则是请求返回之后的处理流程了。
ResultSetHandler–结果集处理器
当我们使用ResultMap或是ResultType配置时,其实就是配置映射规则,ResultSetHandler则会对我们配置的规则将结果集映射成相应的对象。
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
public interface ResultSetHandler {
// 结果集映射成相应的对象集合
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 结果集映射成游标对象
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 存储过程参数处理
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
public interface ResultSetHandler {
// 结果集映射成相应的对象集合
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 结果集映射成游标对象
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 存储过程参数处理
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
该接口也有一个默认实现:DefaultResultSetHandler。对结果集的映射可以说是Mybatis相对来说比较复杂的一个过程,因为它需要对各种场景结果进行处理,其中还有嵌套映射,延迟加载,游标处理等等一些场景,本片介于篇幅并不打算一一展开,点到为止,同样的有兴趣同学可以自己阅读相关源码。
总结
对于插件我们梳理了它的使用注册过程,紧接着我们对插件拦截的核心接口进行了同样的简单介绍,我们能清楚明白我们拦截的接口具体能做什么,只有明白了这些,我们才好定义自己想要的功能。