一 创建SqlSession
通过上文的分析,我们继续解析MyBatis的流程。当解析完mybatis-config.xml和相关的映射文件之后,最后创建的SqlSessionFactory是DefaultSqlSessionFactory,由示例程序可知,接下来就是打开一个SqlSession了:
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
BlogMapper blogMapper = session.getMapper(BlogMapper.class);
SqlSession是与数据库交互的一次会话,而SqlSessionFactory是SqlSession的创建工厂。如下图所示,是SqlSessionFactory的实现:
如下,是SqlSessionFactory的接口声明:
public interface SqlSessionFactory {
/**
* 打开一个SqlSession
* @return SqlSession
*/
SqlSession openSession();
/**
* 打开一个SqlSession
* @param autoCommit 是否自动提交
* @return SqlSession
*/
SqlSession openSession(boolean autoCommit);
/**
* 打开一个SqlSession, 指定Connection
* @param connection Connection
* @return SqlSession
*/
SqlSession openSession(Connection connection);
/**
* 打开一个SqlSession, 指定隔离级别
* @param level 隔离级别
* @return SqlSession
*/
SqlSession openSession(TransactionIsolationLevel level);
/**
* 打开一个SqlSession
* @param execType 执行类型
* @return SqlSession
*/
SqlSession openSession(ExecutorType execType);
/**
* 打开一个SqlSession
* @param execType 执行类型
* @param autoCommit 是否自动提交
* @return SqlSession
*/
SqlSession openSession(ExecutorType execType, boolean autoCommit);
/**
* 打开一个SqlSession
* @param execType 执行类型
* @param level 隔离级别
* @return SqlSession
*/
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
/**
* 打开一个SqlSession
* @param execType 执行类型
* @param connection Connection
* @return SqlSession
*/
SqlSession openSession(ExecutorType execType, Connection connection);
/**
* 获取Configuration配置
* @return Configuration配置对象
*/
Configuration getConfiguration();
}
我们知道这里是DefaultSqlSessionFactory,我么看其openSession() 的实现:
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
/**
* 从DataSource中打开一个SqlSession
* @param execType 类型
* @param level 隔离级别
* @param autoCommit 是否自动提交
* @return SqlSession
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;//事务
try {
final Environment environment = configuration.getEnvironment();//获取环境信息
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//获取事务工厂
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//创建一个事务
final Executor executor = configuration.newExecutor(tx, execType);//创建执行器
return new DefaultSqlSession(configuration, executor, autoCommit);//创建DefaultSqlSession
} catch (Exception e) {//出现异常
closeTransaction(tx); //停止事务
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
这里使用Configuration.defaultExecutorType,而我们在4.1、MyBatis标签解析 1.1.2 初始化Configuration对象 中已经知道,defaultExecutorType是ExecutorType.SIMPLE;然后调用openSessionFromDataSource() 获取SqlSession,并且这里autoCommit(自动提交)为false的,所以如果存在事务的话,这里是需要手动提交事务的。最后创建DefaultSqlSession返回。
1.1 创建事务
在上文中(3.3 MyBatis基础支持层-DataSource、Transaction 二 Transaction),我们知道MyBatis提供了事务接口,我们在mybatis-config.xml配置文件中指定了JDBC类型的事务。这里通过DefaultSqlSessionFactory.getTransactionFactoryFromEnvironment() 方法获取创建事务的工厂:
/**
* 获取TransactionFactory
* @param environment environment
* @return TransactionFactory
*/
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
主要是通过Environment对象获取事务工厂,而我们在4.1、MyBatis标签解析 1.2.8.2 解析transactionManager标签 中已经介绍了这里使用的其实是JdbcTransactionFactory,这里不做重复介绍。
然后通过JdbcTransactionFactory创建一个Transaction,这里创建的是JdbcTransaction:
/**
* 创建一个JdbcTransaction事务
* @param ds 数据源
* @param level 事务隔离级别
* @param autoCommit 是否自动提交
* @return Transaction
*/
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
这里的level为空,autoCommit为false。
1.2 创建执行器Executor
Executor是MyBatis核心接口之一,其中定义了数据库操作的基本方法。在实际应用中经常涉及SqlSession接口的功能,都是基于Executor接口实现的。Executor接口声明如下:
public interface Executor {
/** 空的ResultHandler */
ResultHandler NO_RESULT_HANDLER = null;
/**
* 更新操作
* @param ms MappedStatement
* @param parameter 参数
* @return 更新成功的数量
* @throws SQLException 异常
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* 查询操作
* @param ms MappedStatement
* @param parameter 参数
* @param rowBounds 绑定的参数
* @param resultHandler 结果集处理器
* @param cacheKey 缓存的Key
* @param boundSql 绑定的SQL
* @param <E> 类型
* @return 集合
* @throws SQLException 异常
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
/**
* 查询操作
* @param ms MappedStatement
* @param parameter 参数
* @param rowBounds 绑定的参数
* @param resultHandler 结果集处理器
* @param <E> 类型
* @return 集合
* @throws SQLException 异常
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
/**
* 查询操作
* @param ms MappedStatement
* @param parameter 参数
* @param rowBounds 绑定的参数
* @param <E> 类型
* @return Cursor
* @throws SQLException 异常
*/
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
/**
* 刷新
* @return BatchResult
* @throws SQLException 异常
*/
List<BatchResult> flushStatements() throws SQLException;
/**
* 提交
* @param required 是否强制
* @throws SQLException 异常
*/
void commit(boolean required) throws SQLException;
/**
* 回滚
* @param required 是否强制
* @throws SQLException 异常
*/
void rollback(boolean required) throws SQLException;
/**
* 创建缓存Key
* @param ms MappedStatement
* @param parameterObject 参数
* @param rowBounds RowBounds
* @param boundSql BoundSql
* @return CacheKey
*/
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
/**
* 是否有缓存
* @param ms MappedStatement
* @param key 缓存key
* @return true/false
*/
boolean isCached(MappedStatement ms, CacheKey key);
/**
* 清除本地缓存
*/
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
/**
* 获取事务对象
* @return Transaction
*/
Transaction getTransaction();
/**
* 关闭当前会话
* @param forceRollback 是否回滚
*/
void close(boolean forceRollback);
/**
* 当前会话是否关闭
* @return true/false
*/
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
如下图所示,是Executor的实现:
这里主要使用了模板模式和装饰器模式,CachingExecutor扮演了装饰器的角色,为Executor添加了二级缓存的功能。我们先不看具体的实现,等真正执行使用时再做详细介绍。我们先不看MyBatis的创建Executor流程,我们先看Executor的相关实现。
1.2.1 BaseExecutor及一级缓存简介
BaseExecutor是实现了Executor接口的抽象类,它实现了Executor接口的大部分方法,其中就是使用了模板方法模式。BaseExecutor中主要提供了缓存管理和事务管理的基本功能,继承BaseExecutor的子类只要实现四个基本方法来完成数据库的相关操作即可,这四个方法分别是:doUpdate()方法、doQuery()方法、doQueryCursor()方法、doFlushStatement()方法,其余的功能在BaseExecutor中实现。BaseExecutor中各个字段含义如下:
/** 事务对象 */
protected Transaction transaction;
/** 封装的Executor对象 */
protected Executor wrapper;
/** 延迟加载队列 */
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
/** 一级缓存, 用于缓存该Executor对象查询结果集映射得到的结果对象 */
protected PerpetualCache localCache;
/** 存储过程输出参数缓存 */
protected PerpetualCache localOutputParameterCache;
/** 配置对象 */
protected Configuration configuration;
/** 查询栈的层次 */
protected int queryStack;
/** 是否已经关闭 */
private boolean closed;
1.2.1.1 一级缓存
在常见的企业级系统中,数据库是比较珍贵的资源,很容易成为整个系统的瓶颈。在设计和维护系统时,会进行多方面的权衡,减少对数据库的直接访问。使用缓存是一种比较有效的优化手段,使用缓存可以减少应用系统与数据库的网络交互、减少数据库访问次数、减低数据库的负担等一系列开销,从而提高整个系统的性能。
MyBatis也提供了缓存的功能,其缓存设计为两层结构,分别为一级缓存和二级缓存,二级缓存在后面介绍。
一级缓存是会话级别的缓存,在MyBatis中每创建一个SqlSession对象,就表示开始一次数据库会话。在一次会话中,应用程序可能会在短时间内,例如一个事务内,反复执行完全相同的查询语句,如果不对数据库进行缓存,那么每次查询都会执行一次数据库查询操作,而多次完全相同的、时间间隔较短的查询语句得到的结果集有可能完全相同,这也造成了数据库资源的浪费。
MyBatis中的SqlSession是通过Executor对象完成数据库操作的,为了避免上述问题,在Executor对象中会建立一个简单的缓存,也就是一级缓存,它会将每次查询的结果对象缓存起来,在执行查询操作时,会先查询一级缓存,如果其中存在完全一样的查询语句,则直接从一级缓存中取出相应的结果对象并返回给用户。
一级缓存的生命周期与SqlSession相同,其实也就是与SqlSession中封装的Executor对象的生命周期相同。当调用Executor对象的close()方法时,该Executor对象对应的一级缓存就变得不可用。一级缓存中对象的存活时间受很多方面的影响,例如,在调用Executor.update()方法时,就会先清空一级缓存。
1.2.1.2 一级缓存管理
执行select语句查询数据库是最常用的功能,BaseExecutor.query() 方法实现该功能,代码如下:
@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());
//如果已经关闭则直接抛出异常 Start
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//如果已经关闭则直接抛出异常 End
//清空缓存(第一次查询或者配置了刷新缓存) Start
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
//清空缓存(第一次查询或者配置了刷新缓存) End
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) {
//延迟加载 Start
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
//延迟加载 End
deferredLoads.clear();
//缓存级别是SQL语句级别也清除缓存 Start
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
//缓存级别是SQL语句级别也清除缓存 End
}
return list;
}
我们先不看具体的实现,可以看到,首先是通过CacheKey从localCache(PerpetualCache对象)缓存中获取数据,如果存在就直接返回;否则才会去查询数据库。这个localCache其实就是一级缓存。其执行流程如下图所示:
而对于具体的细节,我们稍后在流程中再具体介绍。
1.2.2 SimpleExecutor
SimpleExecutor继承了BaseExecutor抽象类,它是最简单的Executor接口实现,一级缓存等固定不变的操作都封装到了BaseExecutor中,在SimpleExecutor中不必关系一级缓存等操作,只需要关注实现4个基本方法的实现即可。
如下是SimpleExecutor.doQuery() 方法的具体实现:
/**
* 执行查询
* @param ms MappedStatement
* @param parameter 参数
* @param rowBounds RowBound
* @param resultHandler 结果处理器
* @param boundSql BoundSql
* @param <E> 类型
* @return 集合
* @throws SQLException 异常
*/
@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();//获取Configuration对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());//准备Statement
return handler.query(stmt, resultHandler);//执行查询并对结果集进行处理ResultSetHandler
} finally {
closeStatement(stmt);
}
}
对于doQueryCursor()、update()这里就不做介绍了,可以自行研究。
而对于ReuseExecutor和BatchExecutor我这里也不做单独介绍了,可自行研究。
1.2.3 CachingExecutor及二级缓存简介
CachingExecutor是一个Executor接口的装饰器,它为Executor对象增加了二级缓存的相关功能。
1.2.3.1 二级缓存简介
MyBatis中提供的二级缓存是应用级别的缓存,它的生命周期与应用程序的生命周期相同,与二级缓存相关的配置有三个:
(1) 首先是在mybatis-config.xml配置文件中的cacheEnabled配置,它是二级缓存的总开关,只有当该配置设置为true时,后面的两项配置才会有效果,具体配置如下:
<!-- 全局配置信息 Start -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 全局配置信息 End -->
(2) 需要在映射文件中配置cache节点或cache-ref节点
如果映射配置文件中配置了这两者中的任一一个节点,则表示开启了二级缓存功能。
(3) 最后一个配置项是select节点中的useCache属性,该属性表示查询操作产生的结果对象是否要保存到二级缓存中。useCache属性的默认值是true。
如下图所示,是二级缓存使用的示意图:
当应用程序通过SqlSession2执行定义在命名空间namespace2中的查询操作时,SqlSession2首先到namespace2对应的二级缓存中查找是否缓存了相应的结果对象。如果没有,则继续到SqlSession2对应的一级缓存中查找是否缓存了相应的结果对象,如果依然没有,则访问数据库获取结果集并映射结果对象返回。最后,该结果对象会记录到SqlSession对应的一级缓存一级namespace2对应的二级缓存中,等待后续使用。由于namespace3与namespace2共享同一个二级缓存对象,所以通过SqlSession3执行命名空间namespace3中的完全相同的查询操作时,可以直接从二级缓存中得到相应的结果对象。
对于各个Executor具体实现我们这里先不做过多解释,等后续进行详细介绍。
1.2.4 创建Executor
到这里,我们已经知道Executor是SqlSession底层与数据库交互的对象。我们回到DefaultSqlSessionFactory.openSessionFromDataSource()方法看创建Executor,具体通过Configuration.newExecutor() 方法创建:
/**
* 创建一个Executor
* @param transaction 事务对象
* @param executorType Executor类型
* @return Executor
*/
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {//BatchExecutor
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {//ReuseExecutor
executor = new ReuseExecutor(this, transaction);
} else {//SimpleExecutor
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {//如果开启了缓存, 使用CachingExecutor
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);//通过调用pluginAll()方法创建Executor的代理对象
return executor;
}
首先根据executorType创建指定的Executor,我们的示例使用的是ExecutorType.SIMPLE,所以这里创建了SimpleExecutor,如果开启了二级缓存的话(cacheEnabled为true)则将SimpleExecutor封装到CachingExecutor中。最后调用拦截器链,如果存在指定的拦截器的话,则获取Executor对象;否则是Executor原对象。
1.2.5 MyBatis插件之Executor拦截器
这里我们接触到了MyBatis的一个拦截器(插件),在上文解析MyBatis的配置文件中(4.1、MyBatis标签解析 1.2.4 解析plugins节点)我们解析了MyBatis的<plugins>标签,而<plugins>标签就是配置拦截器的标签。这里我们具体看一下MyBatis拦截器的使用。
如下,我们编写一个简单的拦截器:
@Intercepts(
{@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class})})
public class DemoPlugin implements Interceptor {
private Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("===================================== 拦截器分隔符 =====================================");
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
System.out.println("===================================== 拦截器分隔符 =====================================");
System.out.println("目标类: " + target.getClass());
}
return Interceptor.super.plugin(target);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
这里首先使用注解 @Intercepts 注解,里面使用 @Signature 表示方法的签名,其属性有:
- type:指定拦截的地方。主要有Executor、ParameterHandler、ResultSetHandler、StatementHandler
- method:指定目标类中的方法
- Executor:update()、query()、flushStatements()、commit()、rollback()、getTransaction()、close()、isClosed()方法
- ParameterHandler:getParameterObject()、setParameters()方法
- ResultSetHandler:handleResultSets()、handleOutputParameters()方法
- StatementHandler:prepare()、parameterize()、batch()、update()、query()方法
- args:方法的参数的类型。由于在目标方法存在很多重载,所以这里需要指定目标方法的参数类型去定位具体的方法
编写好拦截器之后,需要在mybatis-config.xml中进行配置:
<plugins>
<plugin interceptor="com.bianjf.mybatis.mybatis.plugins.DemoPlugin">
<property name="key" value="value"/>
</plugin>
</plugins>
此时,在做查询功能的时候,就会打印相关内容了。具体效果可自行测试,我这里不做演示了。
现在使用我们知道了,并且上文也解析过<plugins>标签了,这里我们先分析Executor拦截器的使用:
executor = (Executor) interceptorChain.pluginAll(executor);//通过调用pluginAll()方法创建Executor的代理对象
通过 4.1、MyBatis标签解析 1.2.4 解析plugins节点 分析我们知道,解析出来的<plugins>标签的内容是存放到Configuration.interceptorChain拦截器执行链中,所以这里调用InterceptorChain.pluginAll() 方法:
/**
* 获取目标对象的代理对象或原对象
* @param target 目标对象
* @return 代理对象或原对象
*/
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {//遍历所有的拦截器
target = interceptor.plugin(target);//调用其plugin()方法判断是否需要进行代理的
}
return target;//返回
}
我们示例中的plugin()方法是调用父类Interceptor.super.plugin()方法的,这个方法主要是获取代理对象的功能,示例中没有做逻辑,直接使用父类的Interceptor.plugin() 方法:
/** 决定是否触发intercept()方法 */
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* 构建代理对象
* @param target 目标对象
* @param interceptor 拦截器
* @return 代理对象
*/
public static Object wrap(Object target, Interceptor interceptor) {
//获取用户自定义Interceptor中的@Signature注解的信息, getSignatureMap()方法负责处理@Signature注解
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();//获取目标Class
//获取目标类型实现的interface(JDK动态代理必须有实现类的interface)
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
//创建JDK动态代理 Start
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
//创建JDK动态代理 End
return target;//没有的话直接返回原对象
}
这里主要做了以下功能:
- getSignatureMap获取方法的签名,这里主要解析@Interceptors和@Signature注解,获取目标类的相关方法。最终封装成Class -> Method集合的关系。等使用时,查询对应的方法是否在集合中,在集合中就会走到代理类中;否则直接走原方法
- MyBatis使用的是JDK动态代理,所以这里需要获取目标target对应的interface,通过getAllInterfaces()方法获取
- 最后进行判断,如果存在signatureMap数据,那么就创建动态代理,使用的代理对象为Plugin;否则直接返回原对象
1.2.5.1 解析方法签名
解析方法签名,返回Map<Class, Set<Method>>集合,代码Plugin.getSignaureMap() 代码如下:
/**
* 获取拦截器中@Signature注解的信息
* @param interceptor 拦截器
* @return keywei@Signature的type属性, value是拦截的方法集合
*/
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);//获取@Intercepts注解
//如果实现了Interceptor接口, 但是没有@Intercepts注解, 则直接报错 Start
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
//如果实现了Interceptor接口, 但是没有@Intercepts注解, 则直接报错 End
Signature[] sigs = interceptsAnnotation.value();//获取@Signature注解
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();//拦截的方法集合
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());//获取对应的Method方法
methods.add(method);//添加到Set集合中
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
getSignatureMap(Interceptor),首先获取Interceptor对应的注解@Intercepts,如果不存在则直接抛出异常;然后获取@Intercepts的value,即@Signature注解数组,遍历@Signature注解数组,获取其type对应的Class,然后获取该Class对应的method,如果method不存在的话,则抛出异常;最后将获取到的Method对象添加到methods的Set集合中。
这里返回的是一个Map集合,后续使用有两个作用:
- 判断Map集合是否有数据,如果有数据则创建代理对象;否则是原对象
- 判断是否存在对应的方法,如果存在对应的方法,则走到拦截器Interceptor.intercept()方法中;否则直接执行对应Method.invoke()
1.2.5.2 获取目标对象的interface
getAllInterfaces() 代码如下:
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
1.2.5.3 Plugin
最后,会创建Plugin代理对象。所以我们知道,Plugin对象肯定是一个InvocationHandler对象,并且重写了invoke() 方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());//获取对应的方法
//如果当前的方法需要被拦截, 则调用interceptorintercept()方法进行拦截处理 Start
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
//如果当前的方法需要被拦截, 则调用interceptorintercept()方法进行拦截处理 End
return method.invoke(target, args);//如果当前调用的方法不用被拦截, 则调用target对象的相应方法
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
这里就是代理对象的执行方法逻辑。首先看对应的Class是否存在Set<Method>集合,如果不存在那么拦截的就不是该方法,所以直接执行method.invoke()方法;如果有Set<Method>集合,并且该集合中包含了method,那么执行我们编写的拦截器Interceptor.intercept()方法,传递的参数的是Invocation对象,该方法中只有一个方法,即执行目标方法:
/**
* 执行方法
* @return 执行的结果
* @throws InvocationTargetException 异常
* @throws IllegalAccessException 异常
*/
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);//执行方法
}
到这里,MyBatis的拦截器逻辑其实就很清楚了:
- 首先编写一个类,实现Interceptor接口,并重写其中的intercept()方法,该方法就是用户编写的拦截逻辑;同时需要配置注解@Intercepts和@Signature以完成拦截作用
- 而通过Interceptor.plugin()方法我们可以决定是否执行Interceptor.intercept()方法,plugin()方法主要判断因素:
- 解析@Intercepts和@Signature后,会有一个Map<Class, Set
>集合;如果执行的目标方法不在解析出来的集合中,那么不创建代理对象,即原对象原方法。最终执行方法也是原方法的Method.invoke() - 如果目标方法在解析出来的Map集合中,那么就会创建代理对象Plugin,而Plugin.invoke()方法是执行Interceptor.intercept()方法,即我们上面编写的拦截器方法
- 解析@Intercepts和@Signature后,会有一个Map<Class, Set
- 创建代理对象Plugin传递的是Invocation对象,通过Invocation对象最终执行到目标方法Method.invoke()中
程序走到这里也就创建好了Executor对象了,接下来就是封装到SqlSession对象中使用了。
1.3 SqlSession
通过上文我们知道,最终创建了一个SqlSession对象(DefaultSqlSession),这是用户可以操作的接口层对象。我们先看一下SqlSession接口的相关实现:
这里只有两个实现。我们再看一下SqlSession的接口声明:
/**
* MyBatis框架主要的执行SQL的接口
* @author Clinton Begin
*/
public interface SqlSession extends Closeable {
/**
* 查询一个对象
* @param statement SQL语句
* @param <T> 对象类型
* @return 类型
*/
<T> T selectOne(String statement);
/**
* 查询一个对象
* @param statement SQL语句
* @param parameter 参数
* @param <T> 对象类型
* @return 类型
*/
<T> T selectOne(String statement, Object parameter);
/**
* 查询一个集合
* @param statement SQL语句
* @param <E> 类型
* @return List集合
*/
<E> List<E> selectList(String statement);
/**
* 查询一个集合
* @param statement SQL语句
* @param parameter 参数
* @param <E> 类型
* @return List集合
*/
<E> List<E> selectList(String statement, Object parameter);
/**
* 查询一个集合
* @param statement SQL语句
* @param parameter 参数
* @param rowBounds 在statement的SQL语句后面执行rowBounds参数
* @param <E> 类型
* @return List集合
*/
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
/**
* 查询一个Map集合。例如查询一个Map<Integer,Author>可以这样: selectMap("selectAuthors", "id")
* @param statement SQL语句
* @param mapKey key
* @param <K> key
* @param <V> value
* @return Map
*/
<K, V> Map<K, V> selectMap(String statement, String mapKey);
/**
* 查询一个Map集合。例如查询一个Map<Integer,Author>可以这样: selectMap("selectAuthors", "id")
* @param statement SQL语句
* @param parameter 参数
* @param mapKey key
* @param <K> key
* @param <V> value
* @return Map
*/
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
/**
* 查询一个Map集合。例如查询一个Map<Integer,Author>可以这样: selectMap("selectAuthors", "id")
* @param statement SQL语句
* @param parameter 参数
* @param mapKey key
* @param rowBounds 绑定的参数
* @param <K> key
* @param <V> value
* @return Map
*/
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
/**
* 查询游标
* @param statement SQL语句
* @param <T> 类型
* @return Cursor
*/
<T> Cursor<T> selectCursor(String statement);
/**
* 查询游标
* @param statement SQL语句
* @param parameter 参数
* @param <T> 类型
* @return Cursor
*/
<T> Cursor<T> selectCursor(String statement, Object parameter);
/**
* 查询游标
* @param statement SQL语句
* @param parameter 参数
* @param rowBounds 绑定的SQL
* @param <T> 类型
* @return Cursor
*/
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
/**
* 查询。将结果封装到ResultHandler中
* @param statement SQL语句
* @param parameter 参数
* @param handler ResultHandler
*/
void select(String statement, Object parameter, ResultHandler handler);
/**
* 查询。将结果封装到ResultHandler中
* @param statement SQL语句
* @param handler ResultHandler
*/
void select(String statement, ResultHandler handler);
/**
* 查询。将结果封装到ResultHandler中
* @param statement SQL语句
* @param parameter 参数
* @param rowBounds 绑定的SQL
* @param handler ResultHandler
*/
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
/**
* insert操作
* @param statement SQL语句
* @return 添加的数量
*/
int insert(String statement);
/**
* insert操作
* @param statement SQL语句
* @param parameter 参数
* @return 添加的数量
*/
int insert(String statement, Object parameter);
/**
* update操作
* @param statement SQL语句
* @return 更新的数量
*/
int update(String statement);
/**
* update操作
* @param statement SQL语句
* @param parameter 参数
* @return 更新的数量
*/
int update(String statement, Object parameter);
/**
* delete操作
* @param statement SQL语句
* @return 删除的数量
*/
int delete(String statement);
/**
* delete操作
* @param statement SQL语句
* @param parameter 参数
* @return 删除的数量
*/
int delete(String statement, Object parameter);
/**
* 事务提交
*/
void commit();
/**
* 事务提交
* @param force 是否强制
*/
void commit(boolean force);
/**
* 事务回滚
*/
void rollback();
/**
* 事务回滚
* @param force 是否强制
*/
void rollback(boolean force);
/**
* 刷新
* @return BatchResult
*/
List<BatchResult> flushStatements();
/**
* Closes the session.
*/
@Override
void close();
/**
* 清空缓存
*/
void clearCache();
/**
* 获取Configuration
* @return Configuration对象
*/
Configuration getConfiguration();
/**
* 获取Mapper
* @param type Mapper
* @param <T> 类型
* @return Mapper对象
*/
<T> T getMapper(Class<T> type);
/**
* 获取数据库连接
* @return Connection
*/
Connection getConnection();
}
这里主要就是对数据库进行增删改查的操作,接下来我们以查询操作为例去分析。这里使用的SqlSession对象是DefaultSqlSession。
二 获取Mapper
在我们使用MyBatis过程中,通常使用方式是通过SqlSession获取我们的Mapper对象,然后执行Mapper对象的相应方法,类似代码如下:
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
BlogMapper blogMapper = session.getMapper(BlogMapper.class);
Blog blog = blogMapper.selectDetails(1);
当我们创建出SqlSession对象(DefaultSqlSession)后,就会调用getMapper() 方法获取Mapper对象:
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
/**
* 获取Mapper代理对象
* @param type 类型
* @param sqlSession SqlSession
* @param <T> 类型
* @return Mapper代理对象
*/
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//获取Mapper代理对象工厂 Start
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {//不存在直接抛异常
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
//获取Mapper代理对象工厂 End
try {
return mapperProxyFactory.newInstance(sqlSession);//创建代理对象
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
这里的代码其实我们在前文已经介绍过了,具体请看: 3.4 MyBatis基础支持层-绑定模块、缓存模块 一 模块绑定 部分。所以,最终会创建Mapper接口的代理对象MapperProxy。我们再回顾一下MapperProxy.invoke() 方法(这里不做细讲,只做流程介绍):
@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不会是一个Object,而是一个interface,所以会走到cacheInvoker()获取MapperMethodInvoker :
/**
* 缓存Method对应的MapperMethodInvoker
* @param method Method
* @return MapperMethodInvoker
* @throws Throwable 异常
*/
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
//先从缓存中获取 Start
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}
//先从缓存中获取 End
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {//是否是默认的方法
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {//非默认的方法
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
而我们业务Mapper的增删改查的方法,一般也不是default,而是一个具体的方法,所以最终会创建PlainMethodInvoker,然后调用其invoke()方法执行真正的增删改查的逻辑。
三 执行
通过上文分析,以我们平时使用MyBatis为例,通常我们的Mapper是一个interface,并且其中封装的方法并不是default的,所以MapperProxy中封装的其实是PlainMethodInvoker,我们看PlainMethodInvoker.invoke() 方法:
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);//调用execute执行
}
/**
* 执行操作
* @param sqlSession SqlSession
* @param args 实参列表
* @return 执行结果
*/
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {//根据Sql语句的类型调用sqlSession指定的方法
case INSERT: {//INSERT插入数据
Object param = method.convertArgsToSqlCommandParam(args);
//调用sqlSession.insert()方法
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {//UPDATE更新数据
Object param = method.convertArgsToSqlCommandParam(args);
//调用sqlSession.update()方法
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {//DELETE删除数据
Object param = method.convertArgsToSqlCommandParam(args);
//调用sqlSession.delete()方法
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT://SELECT查询数据
if (method.returnsVoid() && method.hasResultHandler()) {//返回值是void且ResultSet通过ResultHandler处理的方法
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {//返回值是Collection或者数组
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {//返回值是Map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {//返回值是Cursor(游标)
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;
case FLUSH://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;
}
而MapperMethod.execute()我们在前文已经介绍过了(具体请看: 3.4 MyBatis基础支持层-绑定模块、缓存模块 1.3.3 MapperMethod执行)。我们以查询返回单一对象为例,其他可自行研究,会走到SqlSession.selectOne() 方法,这里的SqlSession就是我们上文中介绍的DefaultSqlSession:
@Override
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);//获取一个List
if (list.size() == 1) {//只有一条
return list.get(0);
} else if (list.size() > 1) {//多条, 抛出异常
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {//为空
return null;
}
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
/**
* 查询一个集合
* @param statement SQL语句
* @param parameter 参数
* @param rowBounds 在statement的SQL语句后面执行rowBounds参数
* @param <E> 类型
* @return List集合
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);//获取MappedStatement
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();
}
}
这里从Configuration对象中获取MappedStatement对象,然后调用executor.query() 方法执行查询,我们看BaseExecutor.query():
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);//获取BoundSql对象
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);//创建一级缓存Key
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);//执行查询
}
首先获取BoundSql,BoundSql其实就是具体的SQL对象,然后创建一级缓存用的CacheKey对象,最后调用BaseExecutor.query()方法执行查询。
3.1 获取BoundSql对象
BoundSql对象就是获取最终的SQL对象,MappedStatement.getBoundSql() 代码如下:
/**
* 获取BoundSql独享
* @param parameterObject 参数
* @return BoundSql
*/
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);//获取BoundSql对象
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();//参数的映射
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
所以,这里主要的逻辑是在sqlSource.getBoundSql()方法,而SqlSource对象其实我们在 4.2 MyBatis标签解析-解析映射配置文件 1.9.4 创建SqlSource 部分已经简单介绍过了,对于动态的Sql封装成DynamicSqlSource对象,而非动态SQL封装成RawSqlSource对象了。对于非动态SQL我这里不做介绍了,这里看DynamicSqlSource对象:
/**
* 通过解析得到BoundSql对象, BoundSql封装了包含"?"占位符的SQL语句以及绑定的实参
* @param parameterObject parameterType对应的对象
* @return BoundSql
*/
@Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);//创建DynamicContext对象
rootSqlNode.apply(context);//执行对应节点(if节点、trim节点等)
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);//创建SqlSourceBuilder对象
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();//参数的Class
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());//解析并获取SqlSource对象
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);//获取BoundSql对象
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
这里的rootSqlNode.apply(),主要就是调用IfSqlNode、ChooseSqlNode、TrimSqlNode对应的apply方法。比如<if test=“”>标签,对应IfSqlNode,主要使用的是OGNL表达式进行应用的,等后续我们再单独讲解OGNL相关内容,主要就是判断,并进行SQL的处理。然后调用SqlSourceBuilder.parse() 进行解析:
/**
* 解析SqlSource
* @param originalSql 原始的SQL
* @param parameterType 参数类型
* @param additionalParameters 附加的参数
* @return SqlSource
*/
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql;
if (configuration.isShrinkWhitespacesInSql()) {
sql = parser.parse(removeExtraWhitespaces(originalSql));
} else {
sql = parser.parse(originalSql);
}
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
这里主要对我们SQL中的#{}占位符进行解析,使用的TokenHandler是ParameterMappingTokenHandler :
@Override
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return "?";
}
可以看出,这里使用JDBC中PreparedStatement中的"?"替换掉#{}占位符,最终创建StaticSqlSource对象。所以这里我们已经将SQL中的标签、占位符等都已经处理成相应的内容了。
3.2 创建CacheKey
CacheKey是一级缓存的Key,BaseExecutor.createCacheKey() 代码如下:
/**
* 创建缓存Key
* @param ms MappedStatement
* @param parameterObject 参数
* @param rowBounds RowBounds
* @param boundSql BoundSql
* @return CacheKey
*/
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
//已经关闭的话就证明SqlSession关闭了 Start
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//已经关闭的话就证明SqlSession关闭了 End
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());//将MappedStatement.id添加到CacheKey中
cacheKey.update(rowBounds.getOffset());//将offset添加到CacheKey中
cacheKey.update(rowBounds.getLimit());//将limit添加到CacheKey中
cacheKey.update(boundSql.getSql());//将SQL语句添加到CacheKey中
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();//获取SQL语句绑定的参数
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();//TypeHandler注册器
//获取用户输入的参数, 添加到CacheKey中 Start
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {//过滤掉输出类型的参数
Object value;
String propertyName = parameterMapping.getProperty();//参数名
if (boundSql.hasAdditionalParameter(propertyName)) {
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);
}
cacheKey.update(value);//将参数添加到CacheKey中
}
}
//获取用户输入的参数, 添加到CacheKey中 End
//如果环境不为空的话则将Environment.id添加到CacheKey中 Start
if (configuration.getEnvironment() != null) {
cacheKey.update(configuration.getEnvironment().getId());
}
//如果环境不为空的话则将Environment.id添加到CacheKey中 End
return cacheKey;
}
从这里可以看出影响CacheKey的元素有很多,参数、sql、环境等。
3.3 执行查询
最后执行查询BaseExecutor.query() :
@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());
//如果已经关闭则直接抛出异常 Start
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//如果已经关闭则直接抛出异常 End
//清空缓存(第一次查询或者配置了刷新缓存) Start
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
//清空缓存(第一次查询或者配置了刷新缓存) End
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) {
//延迟加载 Start
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
//延迟加载 End
deferredLoads.clear();
//缓存级别是SQL语句级别也清除缓存 Start
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
//缓存级别是SQL语句级别也清除缓存 End
}
return list;
}
这里其实我们在上文中已经介绍了,对于一级缓存的详细我们稍后详细介绍。这里主要介绍从数据库中查询内容queryFromDatabase() :
/**
* 从数据库查询
* @param ms MappedStatement
* @param parameter 参数
* @param rowBounds RowBounds
* @param resultHandler ResultHandler
* @param key CacheKey
* @param boundSql BoundSql
* @param <E> 类型
* @return 结果
* @throws SQLException 异常
*/
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;
}
核心方法委托给doQuery(),我们看SimpleExecutor.doQuery():
/**
* 执行查询
* @param ms MappedStatement
* @param parameter 参数
* @param rowBounds RowBound
* @param resultHandler 结果处理器
* @param boundSql BoundSql
* @param <E> 类型
* @return 集合
* @throws SQLException 异常
*/
@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();//获取Configuration对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());//准备Statement
return handler.query(stmt, resultHandler);//执行查询并对结果集进行处理ResultSetHandler
} finally {
closeStatement(stmt);
}
}
注意,这里创建StatementHandler,也存在一个拦截器:
/**
* 创建一个StatementHandler
* @param executor 执行器
* @param mappedStatement MappedStatement
* @param parameterObject 参数
* @param rowBounds RowBounds
* @param resultHandler 结果集处理器
* @param boundSql BoundSql
* @return StatementHandler
*/
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);//创建RoutingStatementHandler
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);//执行所有插件
return statementHandler;
}
具体这里不做介绍了,上文已经分析过Plugin的执行流程了。
准备Statement代码prepareStatement():
/**
* 准备Statement
* @param handler Handler
* @param statementLog 日志
* @return Statement
* @throws SQLException 异常
*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);//获取连接
stmt = handler.prepare(connection, transaction.getTimeout());//获取Statement
handler.parameterize(stmt);
return stmt;
}
最后handler.query(),其实主要就是调用Statement.execute()方法了,然后就是使用ResultSetHandler进行结果集的封装了,结果集的封装可自行阅读相关的代码。
MyBatis的执行流程分析到这里其实已经结束了,至于具体的一些没介绍到的细节可自行阅读,相信读者朋友也有自行阅读MyBatis源码的能力了。
评论区