前言
不知道大家看到这张图感觉怎么样,不是难,一共也没有几个组件,但是真的让我想当头疼,因为在面试的时候,就这张图,对,你没看错,就这几个组件,那是让我相当难受啊
MyBatis中SQL执行的整体过程
在 SqlSession 中,会将执行 SQL 的过程交由Executor执行器去执行,过程大致如下:
1、通过DefaultSqlSessionFactory创建与数据库交互的 SqlSession “会话”,其内部会创建一个Executor执行器对象
2、然后Executor执行器通过StatementHandler创建对应的java.sql.Statement对象,并通过ParameterHandler设置参数,然后执行数据库相关操作
如果是数据库更新操作,则可能需要通过KeyGenerator先设置自增键,然后返回受影响的 行数
如果是数据库查询操作,则需要将数据库返回的ResultSet结果集对象包装ResultSetWrapper,然后通过DefaultResultSetHandler对结果集进行映射,最后返回 Java 对象
上面还涉及到一级缓存、二级缓存和延迟加载等其他处理过程,下面我们来看一下具体的执行过程
SQL执行过程(一)之Executor
在MyBatis的SQL执行过程中,Executor执行器担当着一个重要的角色,相关操作都需要通过它来执行,相当于一个调度器,把SQL语句交给它,它来调用各个组件执行操作
其中一级缓存和二级缓存都是在Executor执行器中完成的
Executor执行器接口的实现类如下图所示:
org.apache.ibatis.executor.BaseExecutor:实现Executor接口,提供骨架方法,支持一级缓存,指定几个抽象的方法交由不同的子类去实现
org.apache.ibatis.executor.SimpleExecutor:继承 BaseExecutor 抽象类,简单的 Executor 实现类(默认)
org.apache.ibatis.executor.ReuseExecutor:继承 BaseExecutor 抽象类,可重用的 Executor 实现类,相比SimpleExecutor,在Statement执行完操作后不会立即关闭,而是缓存起来,执行的SQL作为key,下次执行相同的SQL时优先从缓存中获取Statement对象
org.apache.ibatis.executor.BatchExecutor:继承 BaseExecutor 抽象类,支持批量执行的 Executor 实现类
org.apache.ibatis.executor.CachingExecutor:实现 Executor 接口,支持二级缓存的 Executor 的实现类,实际采用了装饰器模式,装饰对象为左边三个Executor类
Executor
org.apache.ibatis.executor.Executor:执行器接口,代码如下:
public interface Executor {
/**
* ResultHandler 空对象
*/
ResultHandler NO_RESULT_HANDLER = null;
/**
* 更新或者插入或者删除
* 由传入的 MappedStatement 的 SQL 所决定
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* 查询,带 ResultHandler + CacheKey + BoundSql
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey cacheKey, BoundSql boundSql) throws SQLException;
/**
* 查询,带 ResultHandler
*/
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException;
/**
* 查询,返回 Cursor 游标
*/
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
/**
* 刷入批处理语句
*/
List<BatchResult> flushStatements() throws SQLException;
/**
* 提交事务
*/
void commit(boolean required) throws SQLException;
/**
* 回滚事务
*/
void rollback(boolean required) throws SQLException;
/**
* 创建 CacheKey 对象
*/
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
/**
* 判断是否缓存
*/
boolean isCached(MappedStatement ms, CacheKey key);
/**
* 清除本地缓存
*/
void clearLocalCache();
/**
* 延迟加载
*/
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
/**
* 获得事务
*/
Transaction getTransaction();
/**
* 关闭事务
*/
void close(boolean forceRollback);
/**
* 判断事务是否关闭
*/
boolean isClosed();
/**
* 设置包装的 Executor 对象
*/
void setExecutorWrapper(Executor executor);
}
执行器接口定义了操作数据库的相关方法:
- 数据库的读和写操作
- 事务相关
- 缓存相关
- 设置延迟加载
- 设置包装的 Executor 对象
BaseExecutor
org.apache.ibatis.executor.BaseExecutor:实现Executor接口,提供骨架方法,指定几个抽象的方法交由不同的子类去实现,例如:
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds,
BoundSql boundSql) throws SQLException;
上面这四个方法交由不同的子类去实现,分别是:更新数据库、刷入批处理语句、查询数据库和查询数据返回游标
构造方法
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
/**
* 事务对象
*/
protected Transaction transaction;
/**
* 包装的 Executor 对象
*/
protected Executor wrapper;
/**
* DeferredLoad(延迟加载)队列
*/
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
/**
* 本地缓存,即一级缓存,内部就是一个 HashMap 对象
*/
protected PerpetualCache localCache;
/**
* 本地输出类型参数的缓存,和存储过程有关
*/
protected PerpetualCache localOutputParameterCache;
/**
* 全局配置
*/
protected Configuration configuration;
/**
* 记录当前会话正在查询的数量
*/
protected int queryStack;
/**
* 是否关闭
*/
private boolean closed;
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
}
其中上面的属性可根据注释进行查看
一级缓存
这里提一下localCache属性,本地缓存,用于一级缓存,MyBatis的一级缓存是什么呢?
每当我们使用 MyBatis 开启一次和数据库的会话,MyBatis 都会创建出一个 SqlSession 对象,表示与数据库的一次会话,