• mybatis源码学习(二):SQL的执行过程


    从上一篇文章中,我们了解到MapperMethod将SQL的执行交给了sqlsession处理。今天我们继续往下看处理的过程。

    SqlSession接口除了提供获取Configuration,Mapper的方法之外,主要的作用就是提供增删该查的方法。

    /**
     *    Copyright 2009-2016 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.session;
    
    import java.io.Closeable;
    import java.sql.Connection;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.ibatis.cursor.Cursor;
    import org.apache.ibatis.executor.BatchResult;
    
    /**
     * The primary Java interface for working with MyBatis.
     * Through this interface you can execute commands, get mappers and manage transactions.
     *
     * @author Clinton Begin
     */
    public interface SqlSession extends Closeable {
    
      /**
       * Retrieve a single row mapped from the statement key
       * @param <T> the returned object type
       * @param statement
       * @return Mapped object
       */
      <T> T selectOne(String statement);
    
      /**
       * Retrieve a single row mapped from the statement key and parameter.
       * @param <T> the returned object type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @return Mapped object
       */
      <T> T selectOne(String statement, Object parameter);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement, Object parameter);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter,
       * within the specified row bounds.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param rowBounds  Bounds to limit object retrieval
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
    
      /**
       * The selectMap is a special case in that it is designed to convert a list
       * of results into a Map based on one of the properties in the resulting
       * objects.
       * Eg. Return a of Map[Integer,Author] for selectMap("selectAuthors","id")
       * @param <K> the returned Map keys type
       * @param <V> the returned Map values type
       * @param statement Unique identifier matching the statement to use.
       * @param mapKey The property to use as key for each value in the list.
       * @return Map containing key pair data.
       */
      <K, V> Map<K, V> selectMap(String statement, String mapKey);
    
      /**
       * The selectMap is a special case in that it is designed to convert a list
       * of results into a Map based on one of the properties in the resulting
       * objects.
       * @param <K> the returned Map keys type
       * @param <V> the returned Map values type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param mapKey The property to use as key for each value in the list.
       * @return Map containing key pair data.
       */
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
    
      /**
       * The selectMap is a special case in that it is designed to convert a list
       * of results into a Map based on one of the properties in the resulting
       * objects.
       * @param <K> the returned Map keys type
       * @param <V> the returned Map values type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param mapKey The property to use as key for each value in the list.
       * @param rowBounds  Bounds to limit object retrieval
       * @return Map containing key pair data.
       */
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
    
      /**
       * A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
       * @param <T> the returned cursor element type.
       * @param statement Unique identifier matching the statement to use.
       * @return Cursor of mapped objects
       */
      <T> Cursor<T> selectCursor(String statement);
    
      /**
       * A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
       * @param <T> the returned cursor element type.
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @return Cursor of mapped objects
       */
      <T> Cursor<T> selectCursor(String statement, Object parameter);
    
      /**
       * A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
       * @param <T> the returned cursor element type.
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param rowBounds  Bounds to limit object retrieval
       * @return Cursor of mapped objects
       */
      <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
    
      /**
       * Retrieve a single row mapped from the statement key and parameter
       * using a {@code ResultHandler}.
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @param handler ResultHandler that will handle each retrieved row
       */
      void select(String statement, Object parameter, ResultHandler handler);
    
      /**
       * Retrieve a single row mapped from the statement
       * using a {@code ResultHandler}.
       * @param statement Unique identifier matching the statement to use.
       * @param handler ResultHandler that will handle each retrieved row
       */
      void select(String statement, ResultHandler handler);
    
      /**
       * Retrieve a single row mapped from the statement key and parameter
       * using a {@code ResultHandler} and {@code RowBounds}
       * @param statement Unique identifier matching the statement to use.
       * @param rowBounds RowBound instance to limit the query results
       * @param handler ResultHandler that will handle each retrieved row
       */
      void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
    
      /**
       * Execute an insert statement.
       * @param statement Unique identifier matching the statement to execute.
       * @return int The number of rows affected by the insert.
       */
      int insert(String statement);
    
      /**
       * Execute an insert statement with the given parameter object. Any generated
       * autoincrement values or selectKey entries will modify the given parameter
       * object properties. Only the number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @param parameter A parameter object to pass to the statement.
       * @return int The number of rows affected by the insert.
       */
      int insert(String statement, Object parameter);
    
      /**
       * Execute an update statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @return int The number of rows affected by the update.
       */
      int update(String statement);
    
      /**
       * Execute an update statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @param parameter A parameter object to pass to the statement.
       * @return int The number of rows affected by the update.
       */
      int update(String statement, Object parameter);
    
      /**
       * Execute a delete statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @return int The number of rows affected by the delete.
       */
      int delete(String statement);
    
      /**
       * Execute a delete statement. The number of rows affected will be returned.
       * @param statement Unique identifier matching the statement to execute.
       * @param parameter A parameter object to pass to the statement.
       * @return int The number of rows affected by the delete.
       */
      int delete(String statement, Object parameter);
    
      /**
       * Flushes batch statements and commits database connection.
       * Note that database connection will not be committed if no updates/deletes/inserts were called.
       * To force the commit call {@link SqlSession#commit(boolean)}
       */
      void commit();
    
      /**
       * Flushes batch statements and commits database connection.
       * @param force forces connection commit
       */
      void commit(boolean force);
    
      /**
       * Discards pending batch statements and rolls database connection back.
       * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
       * To force the rollback call {@link SqlSession#rollback(boolean)}
       */
      void rollback();
    
      /**
       * Discards pending batch statements and rolls database connection back.
       * Note that database connection will not be rolled back if no updates/deletes/inserts were called.
       * @param force forces connection rollback
       */
      void rollback(boolean force);
    
      /**
       * Flushes batch statements.
       * @return BatchResult list of updated records
       * @since 3.0.6
       */
      List<BatchResult> flushStatements();
    
      /**
       * Closes the session
       */
      @Override
      void close();
    
      /**
       * Clears local session cache
       */
      void clearCache();
    
      /**
       * Retrieves current configuration
       * @return Configuration
       */
      Configuration getConfiguration();
    
      /**
       * Retrieves a mapper.
       * @param <T> the mapper type
       * @param type Mapper interface class
       * @return a mapper bound to this SqlSession
       */
      <T> T getMapper(Class<T> type);
    
      /**
       * Retrieves inner database connection
       * @return Connection
       */
      Connection getConnection();
    }
    View Code

    mybatis默认的sqlsession的实现是DefaultSqlSession,如果使用Spring进行事务管理的话则可能是其他的类型。这里我们就从最简单的实现了解。

    增删改查的方法比较类似,就以select方法为例,作具体了解。

    @Override
      public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);
          executor.query(ms, wrapCollection(parameter), rowBounds, handler);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

    首先是从configuration中根据传入的statement获取MappedStatement对象,这里的statement就是 Mapper的namespace + SQL的id 构成的一个id。configuration会缓存这个id和对应的MappedStatement对象。

    MappedStatement对象是Mybatis中重要的一个对象,用来表示一个XML中的一个SQL节点。我们会在之后再去具体了解及configuration解析XML的过程。

    我们可以看到sqlsession是将查询的相关动作交给Executor。Executor(执行器)同样是Mybatis中一个重要的对象。

    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);
    
    }

    可以看到Executor的主要功能都是和操作数据库相关的,执行查询,关闭链接,获取事务等等。

    BaseExecutor是一个抽象类,提供了Executor的基本实现,并将具体的查询过程作为继续保留为抽象方法。这应用了模版方法。贴出具体代码,注意看四个抽象方法:

    /**
     *    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);
        }
    
      }
    
    }
    View Code

    除了应用模版模式之外,Executor还应用了包装器模式,SimpleExecutor是执行器最基本的实现,其他Executor包装了Executor,将核心的方法委托给内部的executor执行,并在方法执行前,增加了对应特性的功能。比如mybatis的二级缓存就是通过CachingExecutor的实现的,它会在查询前先判断时候有缓存命中,如果没有则继续将查询委托给内部的executor,并在执行后将结果进行缓存并返回结果。

    我们先来看下最简单的实现:

    /**
     *    Copyright 2009-2016 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 org.apache.ibatis.cursor.Cursor;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.logging.Log;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.session.Configuration;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    import org.apache.ibatis.transaction.Transaction;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Collections;
    import java.util.List;
    
    /**
     * @author Clinton Begin
     */
    public class SimpleExecutor extends BaseExecutor {
    
      public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
      }
    
      @Override
      public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.update(stmt);
        } finally {
          closeStatement(stmt);
        }
      }
    
      @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);
        }
      }
    
      @Override
      protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
        Statement stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.<E>queryCursor(stmt);
      }
    
      @Override
      public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
        return Collections.emptyList();
      }
    
      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;
      }
    
    }

    我们以查询方法为例,来看一下doQuery方法。

    这里用到了mybatis中另一个重要的对象,StatementHandler。顾名思义这个对象是用来处理Statement的,而Statement又是我们SQL执行的主角。

    看一下StatementHandler接口:

    public interface StatementHandler {
    //提供了获取Statement方法
      Statement prepare(Connection connection, Integer transactionTimeout)
          throws SQLException;
    //设置参数的方法
      void parameterize(Statement statement)
          throws SQLException;
    //批处理
      void batch(Statement statement)
          throws SQLException;
    //更新操作
      int update(Statement statement)
          throws SQLException;
    //查询操作
      <E> List<E> query(Statement statement, ResultHandler resultHandler)
          throws SQLException;
    
      <E> Cursor<E> queryCursor(Statement statement)
          throws SQLException;
    //获取对应的BoundSql对象,boundsql对象表示一个sql语句,但是还未设置参数
      BoundSql getBoundSql();
    //获取参数处理对象
      ParameterHandler getParameterHandler();
    
    }
    View Code

    StatementHandler也有一个抽象类的实现,BaseStatementHandler,提供了大部分的实现,并留有几个抽象方法(同样是模板模式)。

    StatementHandler之后再做更多了解,先回到doQuery中,用configuration对象的newStatementHandler对象创建出StatementHandler对象。

    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }

    可以看到我们创建的是一个RoutingStatementHandler对象。它实现了StatementHandler接口,并根据MappedStatement的类型,确定不同的委托对象(又是一个代理模式的应用)。

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        //根据不同的类型,确定委托对象的类型
        switch (ms.getStatementType()) {
          case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    
      }

    回到SimpleExecutor的doQuery中来,获得到StatementHandler之后,又通过prepareStatement方法获得Statement对象。该方法首先获取connection对象,然后调用statementHandler的prepare方法获取statement对象,并通过parameterize方法设置参数。

    正如我们上文提到的BaseStatementHandler提供了StatementHandler大部分的实现,并提供了抽象方法让子类定义自己的过程。

    instantiateStatement方法就是子类自己定义获取statement对象的过程。

     SimpleStatementHandler直接通过connection的createStatement方法创建Statement对象,而PrepareStatementHandler需要预编译SQL,因此是通过connection的prepareStatement方法获得PrepareStatement对象,CallableStatementHandler需要对过程进行转储,因此是通过connection的prepareCall方法。

    预编译的prepareStatement需要设置参数,因此还要通过parameterize设置参数。

    最后Statemthandler会通过query方法执行查询。而这个方法就是通过statement.execute()方法执行SQL,获得ResultSet。最后再通过ResultSetHandler将结果集转换成需要的Java对象。

    结果转换的过程会在下下篇文章中介绍,下篇文章先介绍MappedStatement的解析。

    最后我们在梳理下SQL执行过程都通过了哪些对象:

    MapperProxy(Mapper的代理对象)->MapperMethod(对应一个Mapper方法)->SqlSession->executor(执行器)->StatementHandler(Statement处理器)->Statement(最终执行SQL的对象)

  • 相关阅读:
    苹果手机的SB系列(9)移动版的AAP个个都比桌面版大?
    初识Redis(四)
    初识Redis(三)
    初识Redis(二)
    初识Redis(一)
    Codeforces Round #589 (Div. 2)
    康拓展开学习笔记
    最短路模板
    K
    D
  • 原文地址:https://www.cnblogs.com/insaneXs/p/9070398.html
Copyright © 2020-2023  润新知