美文网首页
Mybatis源码

Mybatis源码

作者: Zeppelin421 | 来源:发表于2019-10-24 11:40 被阅读0次

    配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.zhaopin.zhiq.dao.TestDao">
        <parameterMap id="testParameterMap" type="com.zhaopin.zhiq.dao.Test">
            <parameter property="id" resultMap="" javaType="" typeHandler="" jdbcType="" mode="" scale=""></parameter>
            <parameter property="name" resultMap="" javaType="" typeHandler="" jdbcType="" mode="" scale=""></parameter>
        </parameterMap>
        
        <sql id="testJoins">
            JOIN sys_office o ON o.id = a.office_id
        </sql>
        
        <select id="get" resultType="Test">
            SELECT * FROM sys_user a WHERE a.id = #{id}
        </select>
        
        <select id="findList" resultType="Test">
            SELECT 
                    a.id,
                    a.name
            FROM sys_user a
            WHERE a."isValid" = 1
            <if test="name != null and name != ''">
                AND a.name LIKE 
                <if test="dbName == 'oracle'">'%'||#{name}||'%'</if>
                <if test="dbName == 'mysql'">CONCAT('%', #{name}, '%')</if>
            </if>
            ORDER BY a.id DESC
        </select>
        
        <insert id="insert" parameterMap="testParameterMap">
            INSERT INTO sys_user(
                id, 
                name
            ) VALUES (
                #{id}, 
                #{name}
            )
        </insert>
    </mapper>
    

    Builder模式

    https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/mapping/ParameterMap.java

    public class ParameterMapping {
    
      private Configuration configuration;
    
      private String property;
      private ParameterMode mode;
      private Class<?> javaType = Object.class;
      private JdbcType jdbcType;
      private Integer numericScale;
      private TypeHandler<?> typeHandler;
      private String resultMapId;
      private String jdbcTypeName;
      private String expression;
    
      private ParameterMapping() {
      }
    
      public static class Builder {
        private ParameterMapping parameterMapping = new ParameterMapping();
    
        public Builder(Configuration configuration, String property, TypeHandler<?> typeHandler) {
          parameterMapping.configuration = configuration;
          parameterMapping.property = property;
          parameterMapping.typeHandler = typeHandler;
          parameterMapping.mode = ParameterMode.IN;
        }
    
        public Builder(Configuration configuration, String property, Class<?> javaType) {
          parameterMapping.configuration = configuration;
          parameterMapping.property = property;
          parameterMapping.javaType = javaType;
          parameterMapping.mode = ParameterMode.IN;
        }
    
        public Builder mode(ParameterMode mode) {
          parameterMapping.mode = mode;
          return this;
        }
    
        public Builder javaType(Class<?> javaType) {
          parameterMapping.javaType = javaType;
          return this;
        }
    
        public Builder jdbcType(JdbcType jdbcType) {
          parameterMapping.jdbcType = jdbcType;
          return this;
        }
    
        public Builder numericScale(Integer numericScale) {
          parameterMapping.numericScale = numericScale;
          return this;
        }
    
        public Builder resultMapId(String resultMapId) {
          parameterMapping.resultMapId = resultMapId;
          return this;
        }
    
        public Builder typeHandler(TypeHandler<?> typeHandler) {
          parameterMapping.typeHandler = typeHandler;
          return this;
        }
    
        public Builder jdbcTypeName(String jdbcTypeName) {
          parameterMapping.jdbcTypeName = jdbcTypeName;
          return this;
        }
    
        public Builder expression(String expression) {
          parameterMapping.expression = expression;
          return this;
        }
    
        public ParameterMapping build() {
          resolveTypeHandler();
          validate();
          return parameterMapping;
        }
      }
    
      @Override
      public String toString() {
        final StringBuilder sb = new StringBuilder("ParameterMapping{");
        //sb.append("configuration=").append(configuration); // configuration doesn't have a useful .toString()
        sb.append("property='").append(property).append('\'');
        sb.append(", mode=").append(mode);
        sb.append(", javaType=").append(javaType);
        sb.append(", jdbcType=").append(jdbcType);
        sb.append(", numericScale=").append(numericScale);
        //sb.append(", typeHandler=").append(typeHandler); // typeHandler also doesn't have a useful .toString()
        sb.append(", resultMapId='").append(resultMapId).append('\'');
        sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\'');
        sb.append(", expression='").append(expression).append('\'');
        sb.append('}');
        return sb.toString();
      }
    }
    

    https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java

    public class SqlSourceBuilder extends BaseBuilder {
    
      private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
    
      public SqlSourceBuilder(Configuration configuration) {
        super(configuration);
      }
    
      public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
        GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
        String sql = parser.parse(originalSql);
        return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
      }
    
      private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
    
        private List<ParameterMapping> parameterMappings = new ArrayList<>();
        private Class<?> parameterType;
        private MetaObject metaParameters;
    
        public ParameterMappingTokenHandler(Configuration configuration, Class<?> parameterType, Map<String, Object> additionalParameters) {
          super(configuration);
          this.parameterType = parameterType;
          this.metaParameters = configuration.newMetaObject(additionalParameters);
        }
    
        public List<ParameterMapping> getParameterMappings() {
          return parameterMappings;
        }
    
        @Override
        public String handleToken(String content) {
          parameterMappings.add(buildParameterMapping(content));
          return "?";
        }
    
        private ParameterMapping buildParameterMapping(String content) {
          Map<String, String> propertiesMap = parseParameterMapping(content);
          String property = propertiesMap.get("property");
          Class<?> propertyType;
          if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
            propertyType = metaParameters.getGetterType(property);
          } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
            propertyType = parameterType;
          } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
            propertyType = java.sql.ResultSet.class;
          } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
            propertyType = Object.class;
          } else {
            MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
            if (metaClass.hasGetter(property)) {
              propertyType = metaClass.getGetterType(property);
            } else {
              propertyType = Object.class;
            }
          }
          ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
          Class<?> javaType = propertyType;
          String typeHandlerAlias = null;
          for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
            String name = entry.getKey();
            String value = entry.getValue();
            if ("javaType".equals(name)) {
              javaType = resolveClass(value);
              builder.javaType(javaType);
            } else if ("jdbcType".equals(name)) {
              builder.jdbcType(resolveJdbcType(value));
            } else if ("mode".equals(name)) {
              builder.mode(resolveParameterMode(value));
            } else if ("numericScale".equals(name)) {
              builder.numericScale(Integer.valueOf(value));
            } else if ("resultMap".equals(name)) {
              builder.resultMapId(value);
            } else if ("typeHandler".equals(name)) {
              typeHandlerAlias = value;
            } else if ("jdbcTypeName".equals(name)) {
              builder.jdbcTypeName(value);
            } else if ("property".equals(name)) {
              // Do Nothing
            } else if ("expression".equals(name)) {
              throw new BuilderException("Expression based parameters are not supported yet");
            } else {
              throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + PARAMETER_PROPERTIES);
            }
          }
          if (typeHandlerAlias != null) {
            builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
          }
          return builder.build();
        }
    
        private Map<String, String> parseParameterMapping(String content) {
          try {
            return new ParameterExpression(content);
          } catch (BuilderException ex) {
            throw ex;
          } catch (Exception ex) {
            throw new BuilderException("Parsing error was found in mapping #{" + content + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
          }
        }
      }
    
    }
    

    https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicSqlSource.java

    public class DynamicSqlSource implements SqlSource {
    
      private final Configuration configuration;
      private final SqlNode rootSqlNode;
    
      public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
      }
    
      @Override
      public BoundSql getBoundSql(Object parameterObject) {
        DynamicContext context = new DynamicContext(configuration, parameterObject);
        rootSqlNode.apply(context);
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
          boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
        }
        return boundSql;
      }
    
    }
    

    工厂模式

    public interface SqlSession extends Closeable {
      <T> T selectOne(String statement);
    
      <T> T selectOne(String statement, Object parameter);
    
      <E> List<E> selectList(String statement);
    
      <E> List<E> selectList(String statement, Object parameter);
    
      <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
    
      <K, V> Map<K, V> selectMap(String statement, String mapKey);
    
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
    
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
    
      <T> Cursor<T> selectCursor(String statement);
    
      <T> Cursor<T> selectCursor(String statement, Object parameter);
    
      <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
    
      void select(String statement, Object parameter, ResultHandler handler);
    
      void select(String statement, ResultHandler handler);
    
      void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
    
      int insert(String statement);
    
      int insert(String statement, Object parameter);
    
      int update(String statement);
    
      int update(String statement, Object parameter);
    
      int delete(String statement);
    
      int delete(String statement, Object parameter);
    
      void commit();
    
      void commit(boolean force);
    
      void rollback();
    
      void rollback(boolean force);
    
      List<BatchResult> flushStatements();
    
      @Override
      void close();
    
      void clearCache();
    
      Configuration getConfiguration();
    
      <T> T getMapper(Class<T> type);
    
      Connection getConnection();
    }
    /**
     *
     * The default implementation for {@link SqlSession}
     */
    public class DefaultSqlSession implements SqlSession {
    }
    
    public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
      private final Configuration configuration;
    
      public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
      }
    
      @Override
      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
    
      @Override
      public SqlSession openSession(boolean autoCommit) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType) {
        return openSessionFromDataSource(execType, null, false);
      }
    
      @Override
      public SqlSession openSession(TransactionIsolationLevel level) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
        return openSessionFromDataSource(execType, level, false);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
        return openSessionFromDataSource(execType, null, autoCommit);
      }
    
      @Override
      public SqlSession openSession(Connection connection) {
        return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType, Connection connection) {
        return openSessionFromConnection(execType, connection);
      }
    
      @Override
      public Configuration getConfiguration() {
        return configuration;
      }
    
      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);
          final Executor executor = configuration.newExecutor(tx, execType);
          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();
        }
      }
    
      private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
        try {
          boolean autoCommit;
          try {
            autoCommit = connection.getAutoCommit();
          } catch (SQLException e) {
            // Failover to true, as most poor drivers
            // or databases won't support transactions
            autoCommit = true;
          }      
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          final Transaction tx = transactionFactory.newTransaction(connection);
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
      private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
        if (environment == null || environment.getTransactionFactory() == null) {
          return new ManagedTransactionFactory();
        }
        return environment.getTransactionFactory();
      }
    
      private void closeTransaction(Transaction tx) {
        if (tx != null) {
          try {
            tx.close();
          } catch (SQLException ignore) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }
    
    }
    
    
    public class SqlSessionFactoryBuilder {
    
      public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
      }
    
      public SqlSessionFactory build(Reader reader, String environment) {
        return build(reader, environment, null);
      }
    
      public SqlSessionFactory build(Reader reader, Properties properties) {
        return build(reader, null, properties);
      }
    
      public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            reader.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }
    
      public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
      }
    
      public SqlSessionFactory build(InputStream inputStream, String environment) {
        return build(inputStream, environment, null);
      }
    
      public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return build(inputStream, null, properties);
      }
    
      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            inputStream.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }
        
      public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }
    
    }
    

    代理模式

    public class MapperProxy<T> implements InvocationHandler, Serializable {
    
      private final SqlSession sqlSession;
      private final Class<T> mapperInterface;
      private final Map<Method, MapperMethod> methodCache;
    
      public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
      }
    
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
      }
    
      private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
          mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
          methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
      }
    
      @UsesJava7
      private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
          throws Throwable {
        final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
            .getDeclaredConstructor(Class.class, int.class);
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        final Class<?> declaringClass = method.getDeclaringClass();
        return constructor
            .newInstance(declaringClass,
                MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                    | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
            .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
      }
    
      /**
       * Backport of java.lang.reflect.Method#isDefault()
       */
      private boolean isDefaultMethod(Method method) {
        return ((method.getModifiers()
            & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
            && method.getDeclaringClass().isInterface();
      }
    }
    
    public class MapperProxyFactory<T> {
    
      private final Class<T> mapperInterface;
      private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
    
      public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      public Class<T> getMapperInterface() {
        return mapperInterface;
      }
    
      public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
      }
    
      @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }
    
      public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
      }
    
    }
    
    public class MapperMethod {
    
      private final SqlCommand command;
      private final MethodSignature method;
    
      public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, mapperInterface, method);
      }
    
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
          case INSERT: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
          }
          case UPDATE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
          }
          case DELETE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
          }
          case SELECT:
            if (method.returnsVoid() && method.hasResultHandler()) {
              executeWithResultHandler(sqlSession, args);
              result = null;
            } else if (method.returnsMany()) {
              result = executeForMany(sqlSession, args);
            } else if (method.returnsMap()) {
              result = executeForMap(sqlSession, args);
            } else if (method.returnsCursor()) {
              result = executeForCursor(sqlSession, args);
            } else {
              Object param = method.convertArgsToSqlCommandParam(args);
              result = sqlSession.selectOne(command.getName(), param);
            }
            break;
          case FLUSH:
            result = sqlSession.flushStatements();
            break;
          default:
            throw new BindingException("Unknown execution method for: " + command.getName());
        }
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
          throw new BindingException("Mapper method '" + command.getName() 
              + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
        }
        return result;
      }
    
      private Object rowCountResult(int rowCount) {
        final Object result;
        if (method.returnsVoid()) {
          result = null;
        } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
          result = rowCount;
        } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
          result = (long)rowCount;
        } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
          result = rowCount > 0;
        } else {
          throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
        }
        return result;
      }
    
      private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
        MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
        if (void.class.equals(ms.getResultMaps().get(0).getType())) {
          throw new BindingException("method " + command.getName() 
              + " needs either a @ResultMap annotation, a @ResultType annotation," 
              + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
        }
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
          RowBounds rowBounds = method.extractRowBounds(args);
          sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
        } else {
          sqlSession.select(command.getName(), param, method.extractResultHandler(args));
        }
      }
    
      private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        List<E> result;
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
          RowBounds rowBounds = method.extractRowBounds(args);
          result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
        } else {
          result = sqlSession.<E>selectList(command.getName(), param);
        }
        // issue #510 Collections & arrays support
        if (!method.getReturnType().isAssignableFrom(result.getClass())) {
          if (method.getReturnType().isArray()) {
            return convertToArray(result);
          } else {
            return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
          }
        }
        return result;
      }
    
      private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
        Cursor<T> result;
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
          RowBounds rowBounds = method.extractRowBounds(args);
          result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);
        } else {
          result = sqlSession.<T>selectCursor(command.getName(), param);
        }
        return result;
      }
    
      private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
        Object collection = config.getObjectFactory().create(method.getReturnType());
        MetaObject metaObject = config.newMetaObject(collection);
        metaObject.addAll(list);
        return collection;
      }
    
      @SuppressWarnings("unchecked")
      private <E> Object convertToArray(List<E> list) {
        Class<?> arrayComponentType = method.getReturnType().getComponentType();
        Object array = Array.newInstance(arrayComponentType, list.size());
        if (arrayComponentType.isPrimitive()) {
          for (int i = 0; i < list.size(); i++) {
            Array.set(array, i, list.get(i));
          }
          return array;
        } else {
          return list.toArray((E[])array);
        }
      }
    
      private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
        Map<K, V> result;
        Object param = method.convertArgsToSqlCommandParam(args);
        if (method.hasRowBounds()) {
          RowBounds rowBounds = method.extractRowBounds(args);
          result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
        } else {
          result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
        }
        return result;
      }
    
      public static class ParamMap<V> extends HashMap<String, V> {
    
        private static final long serialVersionUID = -2212268410512043556L;
    
        @Override
        public V get(Object key) {
          if (!super.containsKey(key)) {
            throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
          }
          return super.get(key);
        }
    
      }
    
      public static class SqlCommand {
    
        private final String name;
        private final SqlCommandType type;
    
        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
          final String methodName = method.getName();
          final Class<?> declaringClass = method.getDeclaringClass();
          MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
              configuration);
          if (ms == null) {
            if (method.getAnnotation(Flush.class) != null) {
              name = null;
              type = SqlCommandType.FLUSH;
            } else {
              throw new BindingException("Invalid bound statement (not found): "
                  + mapperInterface.getName() + "." + methodName);
            }
          } else {
            name = ms.getId();
            type = ms.getSqlCommandType();
            if (type == SqlCommandType.UNKNOWN) {
              throw new BindingException("Unknown execution method for: " + name);
            }
          }
        }
    
        public String getName() {
          return name;
        }
    
        public SqlCommandType getType() {
          return type;
        }
    
        private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
            Class<?> declaringClass, Configuration configuration) {
          String statementId = mapperInterface.getName() + "." + methodName;
          if (configuration.hasStatement(statementId)) {
            return configuration.getMappedStatement(statementId);
          } else if (mapperInterface.equals(declaringClass)) {
            return null;
          }
          for (Class<?> superInterface : mapperInterface.getInterfaces()) {
            if (declaringClass.isAssignableFrom(superInterface)) {
              MappedStatement ms = resolveMappedStatement(superInterface, methodName,
                  declaringClass, configuration);
              if (ms != null) {
                return ms;
              }
            }
          }
          return null;
        }
      }
    
      public static class MethodSignature {
    
        private final boolean returnsMany;
        private final boolean returnsMap;
        private final boolean returnsVoid;
        private final boolean returnsCursor;
        private final Class<?> returnType;
        private final String mapKey;
        private final Integer resultHandlerIndex;
        private final Integer rowBoundsIndex;
        private final ParamNameResolver paramNameResolver;
    
        public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
          Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
          if (resolvedReturnType instanceof Class<?>) {
            this.returnType = (Class<?>) resolvedReturnType;
          } else if (resolvedReturnType instanceof ParameterizedType) {
            this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
          } else {
            this.returnType = method.getReturnType();
          }
          this.returnsVoid = void.class.equals(this.returnType);
          this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
          this.returnsCursor = Cursor.class.equals(this.returnType);
          this.mapKey = getMapKey(method);
          this.returnsMap = (this.mapKey != null);
          this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
          this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
          this.paramNameResolver = new ParamNameResolver(configuration, method);
        }
    
        public Object convertArgsToSqlCommandParam(Object[] args) {
          return paramNameResolver.getNamedParams(args);
        }
    
        public boolean hasRowBounds() {
          return rowBoundsIndex != null;
        }
    
        public RowBounds extractRowBounds(Object[] args) {
          return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
        }
    
        public boolean hasResultHandler() {
          return resultHandlerIndex != null;
        }
    
        public ResultHandler extractResultHandler(Object[] args) {
          return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
        }
    
        public String getMapKey() {
          return mapKey;
        }
    
        public Class<?> getReturnType() {
          return returnType;
        }
    
        public boolean returnsMany() {
          return returnsMany;
        }
    
        public boolean returnsMap() {
          return returnsMap;
        }
    
        public boolean returnsVoid() {
          return returnsVoid;
        }
    
        public boolean returnsCursor() {
          return returnsCursor;
        }
    
        private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
          Integer index = null;
          final Class<?>[] argTypes = method.getParameterTypes();
          for (int i = 0; i < argTypes.length; i++) {
            if (paramType.isAssignableFrom(argTypes[i])) {
              if (index == null) {
                index = i;
              } else {
                throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
              }
            }
          }
          return index;
        }
    
        private String getMapKey(Method method) {
          String mapKey = null;
          if (Map.class.isAssignableFrom(method.getReturnType())) {
            final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
            if (mapKeyAnnotation != null) {
              mapKey = mapKeyAnnotation.value();
            }
          }
          return mapKey;
        }
      }
    
    }
    

    装饰者模式

    https://github.com/mybatis/mybatis-3/tree/master/src/main/java/org/apache/ibatis/cache

    public interface Cache {
    
      String getId();
    
      void putObject(Object key, Object value);
    
      Object getObject(Object key);
    
      Object removeObject(Object key);
    
      void clear();
    
      int getSize();
    
      ReadWriteLock getReadWriteLock();
    }
    
    public class FifoCache implements Cache {
    
      private final Cache delegate;
      private final Deque<Object> keyList;
      private int size;
    
      public FifoCache(Cache delegate) {
        this.delegate = delegate;
        this.keyList = new LinkedList<Object>();
        this.size = 1024;
      }
    
      @Override
      public String getId() {
        return delegate.getId();
      }
    
      @Override
      public int getSize() {
        return delegate.getSize();
      }
    
      public void setSize(int size) {
        this.size = size;
      }
    
      @Override
      public void putObject(Object key, Object value) {
        cycleKeyList(key);
        delegate.putObject(key, value);
      }
    
      @Override
      public Object getObject(Object key) {
        return delegate.getObject(key);
      }
    
      @Override
      public Object removeObject(Object key) {
        return delegate.removeObject(key);
      }
    
      @Override
      public void clear() {
        delegate.clear();
        keyList.clear();
      }
    
      @Override
      public ReadWriteLock getReadWriteLock() {
        return null;
      }
    
      private void cycleKeyList(Object key) {
        keyList.addLast(key);
        if (keyList.size() > size) {
          Object oldestKey = keyList.removeFirst();
          delegate.removeObject(oldestKey);
        }
      }
    
    }
    
    public class LoggingCache implements Cache {
    
      private final Log log;
      private final Cache delegate;
      protected int requests = 0;
      protected int hits = 0;
    
      public LoggingCache(Cache delegate) {
        this.delegate = delegate;
        this.log = LogFactory.getLog(getId());
      }
    
      @Override
      public String getId() {
        return delegate.getId();
      }
    
      @Override
      public int getSize() {
        return delegate.getSize();
      }
    
      @Override
      public void putObject(Object key, Object object) {
        delegate.putObject(key, object);
      }
    
      @Override
      public Object getObject(Object key) {
        requests++;
        final Object value = delegate.getObject(key);
        if (value != null) {
          hits++;
        }
        if (log.isDebugEnabled()) {
          log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
        }
        return value;
      }
    
      @Override
      public Object removeObject(Object key) {
        return delegate.removeObject(key);
      }
    
      @Override
      public void clear() {
        delegate.clear();
      }
    
      @Override
      public ReadWriteLock getReadWriteLock() {
        return null;
      }
    
      @Override
      public int hashCode() {
        return delegate.hashCode();
      }
    
      @Override
      public boolean equals(Object obj) {
        return delegate.equals(obj);
      }
    
      private double getHitRatio() {
        return (double) hits / (double) requests;
      }
    }
    
    public class LruCache implements Cache {
    
      private final Cache delegate;
      private Map<Object, Object> keyMap;
      private Object eldestKey;
    
      public LruCache(Cache delegate) {
        this.delegate = delegate;
        setSize(1024);
      }
    
      @Override
      public String getId() {
        return delegate.getId();
      }
    
      @Override
      public int getSize() {
        return delegate.getSize();
      }
    
      public void setSize(final int size) {
        keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
          private static final long serialVersionUID = 4267176411845948333L;
    
          @Override
          protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
            boolean tooBig = size() > size;
            if (tooBig) {
              eldestKey = eldest.getKey();
            }
            return tooBig;
          }
        };
      }
    
      @Override
      public void putObject(Object key, Object value) {
        delegate.putObject(key, value);
        cycleKeyList(key);
      }
    
      @Override
      public Object getObject(Object key) {
        keyMap.get(key); //touch
        return delegate.getObject(key);
      }
    
      @Override
      public Object removeObject(Object key) {
        return delegate.removeObject(key);
      }
    
      @Override
      public void clear() {
        delegate.clear();
        keyMap.clear();
      }
    
      @Override
      public ReadWriteLock getReadWriteLock() {
        return null;
      }
    
      private void cycleKeyList(Object key) {
        keyMap.put(key, key);
        if (eldestKey != null) {
          delegate.removeObject(eldestKey);
          eldestKey = null;
        }
      }
    
    }
    

    组合模式

    https://github.com/mybatis/mybatis-3/tree/master/src/main/java/org/apache/ibatis/scripting/xmltags

    public interface SqlNode {
      boolean apply(DynamicContext context);
    }
    
    
    public class MixedSqlNode implements SqlNode {
      private final List<SqlNode> contents;
    
      public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
      }
    
      @Override
      public boolean apply(DynamicContext context) {
        for (SqlNode sqlNode : contents) {
          sqlNode.apply(context);
        }
        return true;
      }
    }
    
    
    public class ChooseSqlNode implements SqlNode {
      private final SqlNode defaultSqlNode;
      private final List<SqlNode> ifSqlNodes;
    
      public ChooseSqlNode(List<SqlNode> ifSqlNodes, SqlNode defaultSqlNode) {
        this.ifSqlNodes = ifSqlNodes;
        this.defaultSqlNode = defaultSqlNode;
      }
    
      @Override
      public boolean apply(DynamicContext context) {
        for (SqlNode sqlNode : ifSqlNodes) {
          if (sqlNode.apply(context)) {
            return true;
          }
        }
        if (defaultSqlNode != null) {
          defaultSqlNode.apply(context);
          return true;
        }
        return false;
      }
    }
    
    public class IfSqlNode implements SqlNode {
      private final ExpressionEvaluator evaluator;
      private final String test;
      private final SqlNode contents;
    
      public IfSqlNode(SqlNode contents, String test) {
        this.test = test;
        this.contents = contents;
        this.evaluator = new ExpressionEvaluator();
      }
    
      @Override
      public boolean apply(DynamicContext context) {
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
          contents.apply(context);
          return true;
        }
        return false;
      }
    
    }
    
    public class XMLScriptBuilder extends BaseBuilder {
    
      private final XNode context;
      private boolean isDynamic;
      private final Class<?> parameterType;
    
      public XMLScriptBuilder(Configuration configuration, XNode context) {
        this(configuration, context, null);
      }
    
      public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
        super(configuration);
        this.context = context;
        this.parameterType = parameterType;
      }
    
      public SqlSource parseScriptNode() {
        List<SqlNode> contents = parseDynamicTags(context);
        MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
        SqlSource sqlSource = null;
        if (isDynamic) {
          sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
        } else {
          sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
        }
        return sqlSource;
      }
    
      List<SqlNode> parseDynamicTags(XNode node) {
        List<SqlNode> contents = new ArrayList<SqlNode>();
        NodeList children = node.getNode().getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
          XNode child = node.newXNode(children.item(i));
          if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
            String data = child.getStringBody("");
            TextSqlNode textSqlNode = new TextSqlNode(data);
            if (textSqlNode.isDynamic()) {
              contents.add(textSqlNode);
              isDynamic = true;
            } else {
              contents.add(new StaticTextSqlNode(data));
            }
          } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
            String nodeName = child.getNode().getNodeName();
            NodeHandler handler = nodeHandlers(nodeName);
            if (handler == null) {
              throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
            }
            handler.handleNode(child, contents);
            isDynamic = true;
          }
        }
        return contents;
      }
    
      NodeHandler nodeHandlers(String nodeName) {
        Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
        map.put("trim", new TrimHandler());
        map.put("where", new WhereHandler());
        map.put("set", new SetHandler());
        map.put("foreach", new ForEachHandler());
        map.put("if", new IfHandler());
        map.put("choose", new ChooseHandler());
        map.put("when", new IfHandler());
        map.put("otherwise", new OtherwiseHandler());
        map.put("bind", new BindHandler());
        return map.get(nodeName);
      }
    
      private interface NodeHandler {
        void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
      }
    
      private class BindHandler implements NodeHandler {
        public BindHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          final String name = nodeToHandle.getStringAttribute("name");
          final String expression = nodeToHandle.getStringAttribute("value");
          final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
          targetContents.add(node);
        }
      }
    
      private class TrimHandler implements NodeHandler {
        public TrimHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          List<SqlNode> contents = parseDynamicTags(nodeToHandle);
          MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
          String prefix = nodeToHandle.getStringAttribute("prefix");
          String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
          String suffix = nodeToHandle.getStringAttribute("suffix");
          String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
          TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
          targetContents.add(trim);
        }
      }
    
      private class WhereHandler implements NodeHandler {
        public WhereHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          List<SqlNode> contents = parseDynamicTags(nodeToHandle);
          MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
          WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
          targetContents.add(where);
        }
      }
    
      private class SetHandler implements NodeHandler {
        public SetHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          List<SqlNode> contents = parseDynamicTags(nodeToHandle);
          MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
          SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
          targetContents.add(set);
        }
      }
    
      private class ForEachHandler implements NodeHandler {
        public ForEachHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          List<SqlNode> contents = parseDynamicTags(nodeToHandle);
          MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
          String collection = nodeToHandle.getStringAttribute("collection");
          String item = nodeToHandle.getStringAttribute("item");
          String index = nodeToHandle.getStringAttribute("index");
          String open = nodeToHandle.getStringAttribute("open");
          String close = nodeToHandle.getStringAttribute("close");
          String separator = nodeToHandle.getStringAttribute("separator");
          ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
          targetContents.add(forEachSqlNode);
        }
      }
    
      private class IfHandler implements NodeHandler {
        public IfHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          List<SqlNode> contents = parseDynamicTags(nodeToHandle);
          MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
          String test = nodeToHandle.getStringAttribute("test");
          IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
          targetContents.add(ifSqlNode);
        }
      }
    
      private class OtherwiseHandler implements NodeHandler {
        public OtherwiseHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          List<SqlNode> contents = parseDynamicTags(nodeToHandle);
          MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
          targetContents.add(mixedSqlNode);
        }
      }
    
      private class ChooseHandler implements NodeHandler {
        public ChooseHandler() {
          // Prevent Synthetic Access
        }
    
        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
          List<SqlNode> whenSqlNodes = new ArrayList<SqlNode>();
          List<SqlNode> otherwiseSqlNodes = new ArrayList<SqlNode>();
          handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
          SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
          ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
          targetContents.add(chooseSqlNode);
        }
    
        private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
          List<XNode> children = chooseSqlNode.getChildren();
          for (XNode child : children) {
            String nodeName = child.getNode().getNodeName();
            NodeHandler handler = nodeHandlers(nodeName);
            if (handler instanceof IfHandler) {
              handler.handleNode(child, ifSqlNodes);
            } else if (handler instanceof OtherwiseHandler) {
              handler.handleNode(child, defaultSqlNodes);
            }
          }
        }
    
        private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
          SqlNode defaultSqlNode = null;
          if (defaultSqlNodes.size() == 1) {
            defaultSqlNode = defaultSqlNodes.get(0);
          } else if (defaultSqlNodes.size() > 1) {
            throw new BuilderException("Too many default (otherwise) elements in choose statement.");
          }
          return defaultSqlNode;
        }
      }
    }
    

    模板方法模式

    https://github.com/mybatis/mybatis-3/tree/master/src/main/java/org/apache/ibatis/executor

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

    相关文章

      网友评论

          本文标题:Mybatis源码

          本文链接:https://www.haomeiwen.com/subject/adfrvctx.html