第五篇 mybatis源码阅读:Executor的设计
一、Executor执行器的作用
Mybatis的增删改查都是通过创建SqlSession对象完成,除了这写SqlSession还需要一些辅助功能,比如提交事务、数据回滚、关闭会话、清除缓存等。下面是DefaultSqlSession的源码,通过DefaultSqlSession的源码,可以发现SqlSession实际上都是将功能委托给Executor完成,Executor的作用就是代替SqlSession完成增删改查、事务提交、数据回滚、关闭执行器等功能,这样下来,Executor就变成了Mybatis的核心。
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; private final boolean autoCommit; private boolean dirty; private List<Cursor<?>> cursorList; public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; } ... // 查询 private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } // 更新 @Override public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } //提交事务 @Override public void commit(boolean force) { try { executor.commit(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } // 回滚 @Override public void rollback(boolean force) { try { executor.rollback(isCommitOrRollbackRequired(force)); dirty = false; } catch (Exception e) { throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } //当你将 ExecutorType 设置为 ExecutorType.BATCH 时,进行批处理刷新 @Override public List<BatchResult> flushStatements() { try { return executor.flushStatements(); } catch (Exception e) { throw ExceptionFactory.wrapException("Error flushing statements. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } //关闭会话 @Override public void close() { try { executor.close(isCommitOrRollbackRequired(false)); closeCursors(); dirty = false; } finally { ErrorContext.instance().reset(); } } //清理缓存 @Override public void clearCache() { executor.clearLocalCache(); } ... }
二、Executor执行器的设计
再来看Executor执行器的设计,SqlSession委托Executor完成数据库操作,再结合上Executor的UML类图,可以知道整个设计如下图,图中Executor接口有CachingExecutor和BaseExecutor,而BaseExecutor又有四个子类实现,其中,ClosedExecutor只是实现了BaseExecutor的close方法,被在异常时调用来关闭会话,不多说,下面就是其它三个执行器的作用:
- SimpleExecutor(简单执行器):默认执行器,每次执行SQL都会创建一个新的Statement,用完关闭。
- ReuseExecutor(复用执行器): 内部维护一个Statement池,达到Statement的复用
- BatchExecutor(批处理执行器): 批量执行SQL
可以看到图中,SqlSession先调用的CachingExecutor执行,而不是BaseExecutor,这个原因后面说,先来看看执行器创建的源码,Executor是在SqlSession创建是之前创建,调用configuration的newExecutor方法,在newExecutor方法中,当cacheEnabled是true,都是创建CachingExecutor,用CachingExecutor装饰BaseExecutor,所以SqlSession中持有的Executor是被CachingExecutor,而SqlSession对象,都是Mapper代理类调用,所以SqlSession进入的第一个执行器是CachingExecutor。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //开启一个事务 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //DefaultSqlSession持有一个Executor对象,所以先创建一个执行器 final Executor executor = configuration.newExecutor(tx, execType); //创建一个默认的SqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { //默认创建SimpleExecutor executor = new SimpleExecutor(this, transaction); } // 默认开启一级缓存(cacheEnabled是全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认true) if (cacheEnabled) { executor = new CachingExecutor(executor); //装饰器模式 CachingExecutor装饰executor } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
再来看CachingExecutor的源码,CachingExecutor继承于Executor接口,以query方法为例,可以发现在CachingExecutor实际只是处理了二级缓存的部分,更新和查询的逻辑都是交给持有的BaseExecutor处理。
public class CachingExecutor implements Executor { private final Executor delegate; // 持有的BaseExecutor执行器 private final TransactionalCacheManager tcm = new TransactionalCacheManager(); ... @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //拼接的SQL BoundSql boundSql = ms.getBoundSql(parameterObject); //缓存key, 由MappedStatement的Id,参数,分页参数,sql组成 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); //判断是否开启了缓存 if (cache != null) { //二级缓存处理逻辑 flushCacheIfRequired(ms); // 清空二级缓存 if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); //获取二级缓存 List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { //缓存为null,委托给BaseExecutor执行查询 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } //未开启二级缓存逻辑 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } }
进入到BaseExecutor, 会使用BaseExecutor的默认实现SimpleExecutor,CachingExecutor首先会进入BaseExecutor的query方法,在BaseExecutor中会处理一级缓存,一级缓存是会话级缓存,默认开启,缓存的key和二级缓存相同,如果没有命中一级缓存,就会调用doQuery方法从数据库中查询,BaseExecutor并没有实现该方法,交由子类实现,BaseExecutor还有一个作用就是获取数据库连接。
public abstract class BaseExecutor implements Executor { private static final Log log = LogFactory.getLog(BaseExecutor.class); protected Transaction transaction; protected Executor wrapper; protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads; protected PerpetualCache localCache; protected PerpetualCache localOutputParameterCache; protected Configuration configuration; ... @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); // 缓存key,这里是一级缓存key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } @SuppressWarnings("unchecked") @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } //如果查询堆栈是0(就是之前没有执行过)且Mapper的flushCacheRequired是true if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; //一级缓存中获取 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { //缓存为空,从数据库获取 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } ... private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 先占位一个key, localCache.putObject(key, EXECUTION_PLACEHOLDER); try { //查询数据库 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { // 异常删除key localCache.removeObject(key); } // 放入缓存 localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; } ... //获取数据连接 protected Connection getConnection(Log statementLog) throws SQLException { Connection connection = transaction.getConnection(); if (statementLog.isDebugEnabled()) { return ConnectionLogger.newInstance(connection, statementLog, queryStack); } else { return connection; } } }
BaseExcutor的子类实现有三个,SimpleExecutor是默认执行器,SimpleExecutor处理查询时每次都会创建一个Statement,用完之后便会关闭,Statement只能使用一次,即使是同一个SQL。
public class SimpleExecutor extends BaseExecutor { ... // 处理数据库查询 @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 预编译创建Statement stmt = prepareStatement(handler, ms.getStatementLog()); //执行查询 return handler.query(stmt, resultHandler); } finally { //关闭连接 closeStatement(stmt); } } // 预编译SQL,返回prepareStatement private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; } ... }
ReuseExecutor(复用执行器)与SimpleExecutor不同的是,ReuseExecutor内部维护了一个HashMap的statement池,sql为key,Statement为value,所以当同一个sql会使用同一个Statement,因为HashMap并不是线程安全,所以仅限于单线程使用ReuseExecutor执行器。
public class ReuseExecutor extends BaseExecutor { private final Map<String, Statement> statementMap = new HashMap<>(); ... @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); Statement stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } ... private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; BoundSql boundSql = handler.getBoundSql(); String sql = boundSql.getSql(); // 判断是否存在该sql的Statement if (hasStatementFor(sql)) { stmt = getStatement(sql); applyTransactionTimeout(stmt); } else { // 没有创建Statement,并放入Statement池中 Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); putStatement(sql, stmt); } handler.parameterize(stmt); return stmt; } ... }
BatchExecutor(批处理执行器)是用来做批量执行SQL的,执行器中维护了一个statementList,BatchExecutor会首先将sql的预编译的Statement都放入statementList中,批处理主要用于update方法,所以关键代码为doUpdate方法,doUpdate方法中并没有执行,而是doFlushStatements方法被调用时执行。
public class BatchExecutor extends BaseExecutor { public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002; private final List<Statement> statementList = new ArrayList<>(); private final List<BatchResult> batchResultList = new ArrayList<>(); private String currentSql; private MappedStatement currentStatement; @Override public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException { final Configuration configuration = ms.getConfiguration(); final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null); final BoundSql boundSql = handler.getBoundSql(); final String sql = boundSql.getSql(); final Statement stmt; if (sql.equals(currentSql) && ms.equals(currentStatement)) { int last = statementList.size() - 1; stmt = statementList.get(last); applyTransactionTimeout(stmt); handler.parameterize(stmt);// fix Issues 322 BatchResult batchResult = batchResultList.get(last); batchResult.addParameterObject(parameterObject); } else { Connection connection = getConnection(ms.getStatementLog()); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); // fix Issues 322 currentSql = sql; currentStatement = ms; //添加Statement statementList.add(stmt); batchResultList.add(new BatchResult(ms, sql, parameterObject)); } handler.batch(stmt); return BATCH_UPDATE_RETURN_VALUE; } @Override public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException { try { List<BatchResult> results = new ArrayList<>(); if (isRollback) { return Collections.emptyList(); } // 批量执行 for (int i = 0, n = statementList.size(); i < n; i++) { Statement stmt = statementList.get(i); applyTransactionTimeout(stmt); BatchResult batchResult = batchResultList.get(i); try { batchResult.setUpdateCounts(stmt.executeBatch()); MappedStatement ms = batchResult.getMappedStatement(); List<Object> parameterObjects = batchResult.getParameterObjects(); KeyGenerator keyGenerator = ms.getKeyGenerator(); if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) { Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator; jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects); } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141 for (Object parameter : parameterObjects) { keyGenerator.processAfter(this, ms, stmt, parameter); } } // Close statement to close cursor #1109 closeStatement(stmt); } catch (BatchUpdateException e) { StringBuilder message = new StringBuilder(); message.append(batchResult.getMappedStatement().getId()) .append(" (batch index #") .append(i + 1) .append(")") .append(" failed."); if (i > 0) { message.append(" ") .append(i) .append(" prior sub executor(s) completed successfully, but will be rolled back."); } throw new BatchExecutorException(message.toString(), e, results, batchResult); } results.add(batchResult); } return results; } finally { for (Statement stmt : statementList) { closeStatement(stmt); } currentSql = null; statementList.clear(); batchResultList.clear(); } }
最后用一张图总结执行器