前言
在了解了MyBatis初始化加载过程后,我们也应该研究看看SQL执行过程是怎样执行?这样我们对于Mybatis的整个执行流程都熟悉了,在开发遇到问题也可以很快定位到问题。
更重要的,在面试中遇到面试官咨询Mybatis的知识点的时候,可以很顺畅的把这一套流程讲出来,面试也会觉得你已掌握Mybatis知识点了。
SQL执行过程简介
经过MyBatis初始化加载Sql执行过程所需的信息后,我们就可以通过 SqlSessionFactory 对象得倒忙SqlSession ,然后执行 SQL 语句了,接下来看看Sql执行具体过程,SQL大致执行流程图如下所示:
接下来我们来看看每个执行链路中的具体执行过程,
SqlSession
SqlSession 是 MyBatis 暴露给外部使用的统一接口层,通过 SqlSessionFactory 创建,且其包含和数据库打交道所有操作接口。
下面通过时序图描述 SqlSession 对象的创建流程:
在生成 SqlSession 的同时,基于 executorType 初始化好 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) {
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) interceptorChain.pluginAll(executor); return executor;
}
最顶层的SqlSession接口已生成,那我们可以来看看sql的执行过程下一步是怎样的呢? 怎样使用代理类 MapperProxy 。
MapperProxy
MapperProxy 是 Mapper 接口与SQL 语句映射的关键,通过 MapperProxy 可以让对应的 SQL 语句跟接口进行绑定的,具体流程如下:
- MapperProxy 代理类生成流程
- MapperProxy 代理类执行操作
MapperProxy 代理类生成流程
其中, MapperRegistry 是 Configuration 的一个属性,在解析配置时候会在 MapperRegistry 中缓存了 MapperProxyFactory 的 knownMappers 变量 Map 集合。
MapperRegistry 会根据 mapper 接口类型获取已缓存的 MapperProxyFactory , MapperProxyFactory 会基于 SqlSession 来生成 MapperProxy 代理对象,
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
} } }
当调用 SqlSession 接口时, MapperProxy 怎么是实现的呢? MyBatis 的 Mapper 接口 是通过动态代理实现的,调用 Mapper 接口的任何方法都会执行 MapperProxy::invoke() 方法,
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//Object类型执行
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//接口默认方法执行
if (method.isDefault()) {
if (privateLookupInMethod == null) {
return this.invokeDefaultMethodJava8(proxy, method, args);
}
return this.invokeDefaultMethodJava9(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
但最终会调用倒忙mapperMethod::execute() 方法执行,主要是判断是 INSERT 、 UPDATE 、 DELETE 、 SELECT 语句去操作,其中如果是查询的话,还会判断返回值的类型。
public Obje