Executor 是 MyBatis 的核心接口之一,其中定义了数据库操作的基本方法。在实际应用中经常涉及的 SqISession 接口的功能,都是基于 Executor 接口实现的。 ```java public interface Executor { ResultHandler NO_RESULT_HANDLER = null; // 执行update、insert、delete三种类型的SQL语句 int update(MappedStatement ms, Object parameter) throws SQLException; // 执行select类型的SQL语句,返回值分为结果对象列表或游标对象 List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; // 批量执行SQL语句 List 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); // 根据CacheKey对象查找缓存 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(); } ``` ## 1 BaseExecutor BaseExecutor 是一个实现了 Executor 接口的抽象类,它实现了 Executor 接口的大部分方法。BaseExecutor 中主要提供了缓存管理和事务管理的基本功能,继承 BaseExecutor 的子类只要实现四个基本方法来完成数据库的相关操作即可,这四个方法分别是:doUpdate()方法、doQuery()方法、doQueryCursor()方法、doFlushStatement()方法。 ```java public abstract class BaseExecutor implements Executor { private static final Log log = LogFactory.getLog(BaseExecutor.class); // 事务对象,用于实现事务的提交、回滚和关闭 protected Transaction transaction; // 其中封装的Executor对象 protected Executor wrapper; // 延迟加载队列 protected ConcurrentLinkedQueue deferredLoads; // 一级缓存,用于缓存该Executor对象查询结果集映射得到的结果对象 protected PerpetualCache localCache; // 一级缓存,用于缓存输出类型的参数 protected PerpetualCache localOutputParameterCache; protected Configuration configuration; // 记录嵌套查询的层数 protected int queryStack; // 是否关闭 private boolean closed; } ``` ### 1.1 一级缓存简介 常见的应用系统中,数据库是比较珍贵的资源,很容易成为整个系统的瓶颈。在设计和维护系统时,会进行多方面的权衡,并且利用多种优化手段,减少对数据库的直接访问。 使用缓存是一种比较有效的优化手段,使用缓存可以减少应用系统与数据库的网络交互、减少数据库访问次数、降低数据库的负担、降低重复创建和销毁对象等一系列开销,从而提高整个系统的性能。 MyBatis 提供的缓存功能,分别为一级缓存和二级缓存。BaseExecutor 主要实现了一级缓存的相关内容。一级缓存是会话级缓存,在 MyBatis 中每创建一个 SqlSession 对象,就表示开启一次数据库会话。在一次会话中,应用程序可能会在短时间内(一个事务内),反复执行完全相同的查询语句,如果不对数据进行缓存,那么每一次查询都会执行一次数据库查询操作,而多次完全相同的、时间间隔较短的查询语句得到的结果集极有可能完全相同,这会造成数据库资源的浪费。 为了避免上述问题,MyBatis 会在 Executor 对象中建立一个简单的一级缓存,将每次查询的结果集缓存起来。在执行查询操作时,会先查询一级缓存,如果存在完全一样的查询情况,则直接从一级缓存中取出相应的结果对象并返回给用户,减少数据库访问次数,从而减小了数据库的压力。 一级缓存的生命周期与 SqlSession 相同,其实也就与 SqISession 中封装的 Executor 对象的生命周期相同。当调用 Executor 对象的 close()方法时(断开连接),该 Executor 对象对应的一级缓存就会被废弃掉。一级缓存中对象的存活时间受很多方面的影响,例如,在调用 Executor 的 update()方法时,也会先请空一级缓存。一级缓存默认是开启的,一般情况下,不需要用户进行特殊配置。 ### 1.2 一级缓存的管理 BaseExecutor 的 query()方法会首先创建 CacheKey 对象,并根据该 CacheKey 对象查找一级缓存,如果缓存命中则返回缓存中记录的结果对象,如果缓存未命中则查询数据库得到结果集,之后将结果集映射成结果对象并保存到一级缓存中,同时返回结果对象。 ```java public abstract class BaseExecutor implements Executor { @Override public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 获取BoundSql对象 BoundSql boundSql = ms.getBoundSql(parameter); // 创建CacheKey对象,该对象由多个参数组装而成 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); // query方法的重载,进行后续处理 return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } @Override public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } // 可以看到CacheKey对象由MappedStatement的id、RowBounds的offset和limit // sql语句(包含占位符"?")、用户传递的实参组成 CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); cacheKey.update(boundSql.getSql()); List parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); // 获取用户传入的实参,并添加到CacheKey对象中 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对象中 cacheKey.update(value); } } // 如果configuration的environment不为空,则将该environment的id // 添加到CacheKey对象中 if (configuration.getEnvironment() != null) { cacheKey.update(configuration.getEnvironment().getId()); } return cacheKey; } @Override public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // 检查当前Executor是否已关闭 ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { // 非嵌套查询,且