diff --git a/docs/Mybatis/核心处理层/5、Executor组件.md b/docs/Mybatis/核心处理层/5、Executor组件.md index e69de29..ed9c9c9 100644 --- a/docs/Mybatis/核心处理层/5、Executor组件.md +++ b/docs/Mybatis/核心处理层/5、Executor组件.md @@ -0,0 +1,289 @@ +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()) { + // 非嵌套查询,且