MyBatis 源码分析笔记 sql 执行

  • 我们可以看到,Executor 的直接子类有 BaseExecutor 和 CachingExecutor 两个。
  • 实际上,CachingExecutor 在 BaseExecutor 的基础上,实现二级缓存功能。
  • 在下文中,BaseExecutor 的本地缓存,就是一级缓存。

每当我们使用 MyBatis 开启一次和数据库的会话,MyBatis 会创建出一个 SqlSession 对象表示一次数据库会话,而每个 SqlSession 都会创建一个 Executor 对象

在对数据库的一次会话中,我们有可能会反复地执行完全相同的查询语句,如果不采取一些措施的话,每一次查询都会查询一次数据库,而我们在极短的时间内做了完全相同的查询,那么它们的结果极有可能完全相同,由于查询一次数据库的代价很大,这有可能造成很大的资源浪费。

为了解决这一问题,减少资源的浪费,MyBatis 会在表示会话的SqlSession 对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。注意,这个“简单的缓存”就是一级缓存,且默认开启,无法关闭

如下图所示,MyBatis 会在一次会话的表示 —— 一个 SqlSession 对象中创建一个本地缓存( localCache ),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。

 

3.5 query

① #query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) 方法,读操作

  • <1> 处,调用 MappedStatement#getBoundSql(Object parameterObject) 方法,获得 BoundSql 对象。
  • <2> 处,调用 #createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) 方法,创建 CacheKey 对象。
  • <3> 处,调用 #query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 方法,读操作。通过这样的方式,两个 #query(...) 方法,实际是统一的。

② #query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

  • <1> 处,已经关闭,则抛出 ExecutorException 异常。
  • <2> 处,调用 #clearLocalCache() 方法,清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。例如:<select flushCache="true"> ... </a> 。
  • <3> 处,queryStack + 1 。
  • <4.1> 处,从一级缓存 localCache 中,获取查询结果。
    • <4.2> 处,获取到,则进行处理。对于 #handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) 方法,是处理存储过程的情况,所以我们就忽略。
    • <4.3> 处,获得不到,则调用 #queryFromDatabase() 方法,从数据库中查询。详细解析,见 「3.5.1 queryFromDatabase」 。
  • <5> 处,queryStack - 1 。
  • <6.1> 处,遍历 DeferredLoad 队列,逐个调用 DeferredLoad#load() 方法,执行延迟加载。详细解析,见 《精尽 MyBatis 源码分析 —— SQL 执行(五)之延迟加载》 。
    • <6.2> 处,清空 DeferredLoad 队列。
  • <7> 处,如果缓存级别是 LocalCacheScope.STATEMENT ,则调用 #clearLocalCache() 方法,清空本地缓存。默认情况下,缓存级别是 LocalCacheScope.SESSION 

3.5.1 queryFromDatabase

#queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 方法,从数据库中读取操作

3.7 update

#update(MappedStatement ms, Object parameter) 方法,执行写操作

org.apache.ibatis.executor.SimpleExecutor ,继承 BaseExecutor 抽象类,简单的 Executor 实现类。

  • 每次开始读或写操作,都创建对应的 Statement 对象。
  • 执行完成后,关闭该 Statement 对象

org.apache.ibatis.executor.ReuseExecutor ,继承 BaseExecutor 抽象类,可重用的 Executor 实现类。

  • 每次开始读或写操作,优先从缓存中获取对应的 Statement 对象。如果不存在,才进行创建。
  • 执行完成后,不关闭该 Statement 对象。
  • 其它的,和 SimpleExecutor 是一致的。
  • ReuseExecutor 考虑到重用性,但是 Statement 最终还是需要有地方关闭。答案就在 #doFlushStatements(boolean isRollback) 方法中。而 BaseExecutor 在关闭 #close() 方法中,最终也会调用该方法,从而完成关闭缓存的 Statement 对象们
  • 另外,BaseExecutor 在提交或者回滚事务方法中,最终也会调用该方法,也能完成关闭缓存的 Statement 对象们

 

org.apache.ibatis.executor.BatchExecutor ,继承 BaseExecutor 抽象类,批量执行的 Executor 实现类。

 

在上文中提到的一级缓存中,其最大的共享范围就是一个 SqlSession 内部,如果多个 SqlSession 之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用 CachingExecutor 装饰 Executor ,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示。

 

org.apache.ibatis.executor.CachingExecutor ,实现 Executor 接口,支持二级缓存的 Executor 的实现类。

 

  • tcm 属性,TransactionalCacheManager 对象,支持事务的缓存管理器。因为二级缓存是支持跨 Session 进行共享,此处需要考虑事务,那么,必然需要做到事务提交时,才将当前事务中查询时产生的缓存,同步到二级缓存中。这个功能,就通过 TransactionalCacheManager 来实现。
  • <1> 处,设置 delegate 属性,为被委托的 Executor 对象。
  • <2> 处,调用 delegate 属性的 #setExecutorWrapper(Executor executor) 方法,设置 delegate 被当前执行器所包装

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

SQL 执行之 StatementHandler

 SQL 执行 之 ResultSetHandler

org.apache.ibatis.executor.resultset.DefaultResultSetHandler ,实现 ResultSetHandler 接口,默认的 ResultSetHandler 实现类

#handleResultSets(Statement stmt) 方法,处理 java.sql.ResultSet 结果集,转换成映射的对应结果

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值