把源码拆的这么碎,让你彻底明白mybatis缓存

前言

不知道大家看到这张图感觉怎么样,不是难,一共也没有几个组件,但是真的让我想当头疼,因为在面试的时候,就这张图,对,你没看错,就这几个组件,那是让我相当难受啊

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 对象,表示与数据库的一次会话,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值