mybatis源码之执行器解析

☆樱花仙子☆ 提交于 2019-11-29 00:25:12

mytatis执行器主要职责是翻译jdbc操作,是mybatis非常重要的功能

  • 执行器类图如下

-从上图中可以看出所有执行器都实现了Executor接口,定义了一些通用的操作,Executor的接口定义如下

/**  *    Copyright 2009-2015 the original author or authors.  *  *    Licensed under the Apache License, Version 2.0 (the "License");  *    you may not use this file except in compliance with the License.  *    You may obtain a copy of the License at  *  *       http://www.apache.org/licenses/LICENSE-2.0  *  *    Unless required by applicable law or agreed to in writing, software  *    distributed under the License is distributed on an "AS IS" BASIS,  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *    See the License for the specific language governing permissions and  *    limitations under the License.  */ package org.apache.ibatis.executor;  import java.sql.SQLException; import java.util.List;  import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.transaction.Transaction;  /**  * @author Clinton Begin  */ public interface Executor {    ResultHandler NO_RESULT_HANDLER = null;    int update(MappedStatement ms, Object parameter) throws SQLException;    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;    <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 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();    void setExecutorWrapper(Executor executor);  }  

主要的执行器用四种BatchExecutor,SimpleExecutor,ReuseExecutor,CachingExecutor BatchExecutor 主要的功能处理批理操作

SimpleExecutor 默认执行器处理一般的操作

ReuseExecutor 重复使用,该执行器维护了一个sql与Statement对应关系的一个缓存对像,可以复用Statement

CachingExecutor 缓存执行器,TransactionalCacheManager维护缓存对像,一个本地的HashMap,当使用mybatis缓存时由该执行器负责处理

上面四种执行器除CachingExecutor没有继承BaseExecutor外,基它都继承BaseExecutor基本解析器,CachingExecutor具体执行的时候也是依赖于上面三种执行器,所以看看BaseExecutor做了什么操作

 /**  *    Copyright 2009-2017 the original author or authors.  *  *    Licensed under the Apache License, Version 2.0 (the "License");  *    you may not use this file except in compliance with the License.  *    You may obtain a copy of the License at  *  *       http://www.apache.org/licenses/LICENSE-2.0  *  *    Unless required by applicable law or agreed to in writing, software  *    distributed under the License is distributed on an "AS IS" BASIS,  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *    See the License for the specific language governing permissions and  *    limitations under the License.  */ package org.apache.ibatis.executor;  import static org.apache.ibatis.executor.ExecutionPlaceholder.EXECUTION_PLACEHOLDER;  import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue;  import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.cache.impl.PerpetualCache; import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.executor.statement.StatementUtil; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.logging.jdbc.ConnectionLogger; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.ParameterMode; import org.apache.ibatis.mapping.StatementType; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.factory.ObjectFactory; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.LocalCacheScope; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.transaction.Transaction; import org.apache.ibatis.type.TypeHandlerRegistry;  /**  * @author Clinton Begin  */ 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;    protected int queryStack;   private boolean closed;    protected BaseExecutor(Configuration configuration, Transaction transaction) {     this.transaction = transaction;     this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();     this.localCache = new PerpetualCache("LocalCache");     this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");     this.closed = false;     this.configuration = configuration;     this.wrapper = this;   }    @Override   public Transaction getTransaction() {     if (closed) {       throw new ExecutorException("Executor was closed.");     }     return transaction;   }    @Override   public void close(boolean forceRollback) {     try {       try {         rollback(forceRollback);       } finally {         if (transaction != null) {           transaction.close();         }       }     } catch (SQLException e) {       // Ignore.  There's nothing that can be done at this point.       log.warn("Unexpected exception on closing transaction.  Cause: " + e);     } finally {       transaction = null;       deferredLoads = null;       localCache = null;       localOutputParameterCache = null;       closed = true;     }   }    @Override   public boolean isClosed() {     return closed;   }    @Override   public int update(MappedStatement ms, Object parameter) throws SQLException {     ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());     if (closed) {       throw new ExecutorException("Executor was closed.");     }     clearLocalCache();     return doUpdate(ms, parameter);   }    @Override   public List<BatchResult> flushStatements() throws SQLException {     return flushStatements(false);   }    public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {     if (closed) {       throw new ExecutorException("Executor was closed.");     }     return doFlushStatements(isRollBack);   }    @Override   public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {     BoundSql boundSql = ms.getBoundSql(parameter);     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.");     }     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;   }    @Override   public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {     BoundSql boundSql = ms.getBoundSql(parameter);     return doQueryCursor(ms, parameter, rowBounds, boundSql);   }    @Override   public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {     if (closed) {       throw new ExecutorException("Executor was closed.");     }     DeferredLoad deferredLoad = new DeferredLoad(resultObject, property, key, localCache, configuration, targetType);     if (deferredLoad.canLoad()) {       deferredLoad.load();     } else {       deferredLoads.add(new DeferredLoad(resultObject, property, key, localCache, configuration, targetType));     }   }    @Override   public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {     if (closed) {       throw new ExecutorException("Executor was closed.");     }     CacheKey cacheKey = new CacheKey();     cacheKey.update(ms.getId());     cacheKey.update(rowBounds.getOffset());     cacheKey.update(rowBounds.getLimit());     cacheKey.update(boundSql.getSql());     List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();     TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();     // mimic DefaultParameterHandler logic     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.update(value);       }     }     if (configuration.getEnvironment() != null) {       // issue #176       cacheKey.update(configuration.getEnvironment().getId());     }     return cacheKey;   }    @Override   public boolean isCached(MappedStatement ms, CacheKey key) {     return localCache.getObject(key) != null;   }    @Override   public void commit(boolean required) throws SQLException {     if (closed) {       throw new ExecutorException("Cannot commit, transaction is already closed");     }     clearLocalCache();     flushStatements();     if (required) {       transaction.commit();     }   }    @Override   public void rollback(boolean required) throws SQLException {     if (!closed) {       try {         clearLocalCache();         flushStatements(true);       } finally {         if (required) {           transaction.rollback();         }       }     }   }    @Override   public void clearLocalCache() {     if (!closed) {       localCache.clear();       localOutputParameterCache.clear();     }   }    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;    protected void closeStatement(Statement statement) {     if (statement != null) {       try {         if (!statement.isClosed()) {           statement.close();         }       } catch (SQLException e) {         // ignore       }     }   }    /**    * Apply a transaction timeout.    * @param statement a current statement    * @throws SQLException if a database access error occurs, this method is called on a closed <code>Statement</code>    * @since 3.4.0    * @see StatementUtil#applyTransactionTimeout(Statement, Integer, Integer)    */   protected void applyTransactionTimeout(Statement statement) throws SQLException {     StatementUtil.applyTransactionTimeout(statement, statement.getQueryTimeout(), transaction.getTimeout());   }    private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {     if (ms.getStatementType() == StatementType.CALLABLE) {       final Object cachedParameter = localOutputParameterCache.getObject(key);       if (cachedParameter != null && parameter != null) {         final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);         final MetaObject metaParameter = configuration.newMetaObject(parameter);         for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {           if (parameterMapping.getMode() != ParameterMode.IN) {             final String parameterName = parameterMapping.getProperty();             final Object cachedValue = metaCachedParameter.getValue(parameterName);             metaParameter.setValue(parameterName, cachedValue);           }         }       }     }   }    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {     List<E> list;     localCache.putObject(key, EXECUTION_PLACEHOLDER);     try {       list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);     } finally {       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;     }   }    @Override   public void setExecutorWrapper(Executor wrapper) {     this.wrapper = wrapper;   }      private static class DeferredLoad {      private final MetaObject resultObject;     private final String property;     private final Class<?> targetType;     private final CacheKey key;     private final PerpetualCache localCache;     private final ObjectFactory objectFactory;     private final ResultExtractor resultExtractor;      // issue #781     public DeferredLoad(MetaObject resultObject,                         String property,                         CacheKey key,                         PerpetualCache localCache,                         Configuration configuration,                         Class<?> targetType) {       this.resultObject = resultObject;       this.property = property;       this.key = key;       this.localCache = localCache;       this.objectFactory = configuration.getObjectFactory();       this.resultExtractor = new ResultExtractor(configuration, objectFactory);       this.targetType = targetType;     }      public boolean canLoad() {       return localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER;     }      public void load() {       @SuppressWarnings( "unchecked" )       // we suppose we get back a List       List<Object> list = (List<Object>) localCache.getObject(key);       Object value = resultExtractor.extractObjectFromList(list, targetType);       resultObject.setValue(property, value);     }    }  } 

从源码中可以看出来,实现了获取Connection,封装返回结果,一些通抽方法的实现,我们在看下SimpleExecutor的简单操作 查询操作

   @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);       stmt = prepareStatement(handler, ms.getStatementLog());       return handler.<E>query(stmt, resultHandler);     } finally {       closeStatement(stmt);     }   }   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;   } 

看到Statement就明白是怎么回事

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!