模板方法模式

介绍

定义:模板方法模式在一个方法中定义一个算法的骨架,将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义该算法的某些步骤。

示例:

/**
 * 咖啡因饮料冲泡法
 */
public abstract class CaffeineBeverage {
    /**
     * 模板方法,咖啡因饮料有相同的制作过程,只是某些步骤不同
     */
    final void prepareRecipe() {
        // 把水煮沸
        boilWater();
        // 冲泡
        brew();
        // 把饮料倒进杯子
        pourInCup();
        // 加调料
        addCondiments();
    }

    abstract void brew();

    abstract void addCondiments();

    /**
     * 把水煮沸(步骤是通用的,所以放在基类中)
     */
    void boilWater() {
        System.out.println("Boiling water");
    }

    /**
     * 把饮料倒进杯子
     */
    void pourInCup() {
        System.out.println("Pouring into cup");
    }
}

/**
 * 咖啡冲泡法
 */
public class Coffee extends CaffeineBeverage {
    /**
     * 冲泡咖啡
     */
    @Override
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    /**
     * 加入糖和牛奶
     */
    @Override
    void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}

/**
 * 茶冲泡法
 */
public class Tea extends CaffeineBeverage {
    /**
     * 冲泡茶叶
     */
    @Override
    void brew() {
        System.out.println("Steeping the tea");
    }

    /**
     * 加入柠檬
     */
    @Override
    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}

分析:在上述代码中,CaffeineBeverage类是一个抽象类,其中prepareRecipe()方法是一个模板方法,定义了算法的骨架,包含了一些基本的步骤,以及一些需要延迟到子类中实现的抽象方法。在CoffeeTea类中,分别实现了brew()addCondiments()方法,这些方法是具体的步骤,可以根据需要在子类中进行重定义。通过使用模板方法模式,我们可以将算法的结构和具体实现分离开来,从而实现代码的复用和扩展。

示例来自《HeadFirst 设计模式》

在 MyBatis 中的应用

TypeHandler

在 MyBatis 中,TypeHandler 接口用来给 PreparedStatement 设置参数,以及从 ResultSet 获取结果:

public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

TypeHandlerRegistry 中注册了很多类型处理器,比如 StringTypeHandler、IntegerTypeHandler 等:

public final class TypeHandlerRegistry {
  
  // ...

  public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    // ...
  }

  // ...

}

这些类型处理器都继承自 BaseTypeHandler,BaseTypeHandler 实现了 TypeHandler 接口中的方法,但是仅定义了通用的步骤,具体的实现由子类完成:

public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

  protected Configuration configuration;

  public void setConfiguration(Configuration c) {
    this.configuration = c;
  }

  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
                "Cause: " + e, e);
      }
    } else {
      try {
        // ⭐ 设置非 null 参数(子类实现)
        setNonNullParameter(ps, i, parameter, jdbcType);
      } catch (Exception e) {
        throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
                "Try setting a different JdbcType for this parameter or a different configuration property. " +
                "Cause: " + e, e);
      }
    }
  }

  @Override
  public T getResult(ResultSet rs, String columnName) throws SQLException {
    T result;
    try {
      // ⭐ 获取非 null 结果(子类实现)
      result = getNullableResult(rs, columnName);
    } catch (Exception e) {
      throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set.  Cause: " + e, e);
    }
    if (rs.wasNull()) {
      return null;
    } else {
      return result;
    }
  }
  
  // ...

  /**
   * ⭐ 设置非 null 参数(子类实现)
   */
  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * ⭐ 获取非 null 结果(子类实现)
   */
  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

  // ...

}

比如 StringTypeHandler 类继承了 BaseTypeHandler 类,并实现了 getNullableResult() 等方法:

public class StringTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getString(columnName);
  }

  // ...

}

这样做的好处是各种类型的 TypeHandler 都可以通过继承 BaseTypeHandler 类来实现自己的处理逻辑,同时又复用 BaseTypeHandler 类中通用的步骤,既易于扩展,又易于维护。

StatementHandler

MyBatis StatementHandler 接口主要负责 SQL 语句的执行和参数的设置:

public interface StatementHandler {

  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;

  // ...

}

BaseStatementHandler 类是 StatementHandler 接口的抽象实现类,它定义了 StatementHandler 接口需要实现的大部分方法,包括对参数的设置、SQL 语句的执行等,并提供了一个抽象方法 instantiateStatement(Connection connection),供子类实例化 Statement 对象:

public abstract class BaseStatementHandler implements StatementHandler {
  // ...

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      // ⭐ 实例化 Statement 对象(子类实现)
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

  /**
   * ⭐ 实例化 Statement 对象(子类实现)
   */
  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

  // ...
}

StatementHandler 的实现类 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler 分别对应不同的 Statement 对象,比如 SimpleStatementHandler 使用 Statement 对象执行 SQL 语句,PreparedStatementHandler 使用 PreparedStatement 对象执行 SQL 语句,CallableStatementHandler 使用 CallableStatement 对象执行 SQL 语句,它们都继承自 BaseStatementHandler 类,并实现了不同的 instantiateStatement() 方法,以 PreparedStatement 为例:

public class PreparedStatementHandler extends BaseStatementHandler {

  // ...

  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

  // ...

}

可以看到,PreparedStatementHandler 通过调用 connection.prepareStatement() 方法实例化了一个 PreparedStatement 对象,并返回了该对象。

在这里,BaseStatementHandler 中的 prepare() 方法就是模板方法,其中定义了 prepare(准备)的通用步骤,instantiateStatement(实例化 Statement 对象)的细节交给子类实现。

Executor

Executor 接口是 MyBatis 最核心的接口之一,它定义了执行 SQL 语句的方法,包括查询、更新等操作:

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;

  // ...
}

BaseExecutor 类是 Executor 接口的抽象实现类,它定义了 Executor 接口需要实现的大部分方法,包括对 SQL 语句的更新和查询等操作,并提供了抽象方法 doUpdate() 和 doQuery(),供子类实现:

public abstract class BaseExecutor implements Executor {
  @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();
    // ⭐ 执行 update 语句(子类实现)
    return doUpdate(ms, parameter);
  }

  /**
   * ⭐ 执行 update 语句(子类实现)
   */
  protected abstract int doUpdate(MappedStatement ms, Object parameter)
      throws SQLException;

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

  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 abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException;
}

Executor 的实现类 SimpleExecutor、ClosedExecutor、ReuseExecutor、BatchExecutor 都继承自 BaseExecutor,并提供了具体的实现方法 doUpdate() 和 doQuery()。比如 SimpleExecutor 的 doUpdate 方法如下:

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

可以看到,最终调用的是 StatementHandler 的 update() 方法。

posted @ 2024-12-08 20:11  Higurashi-kagome  阅读(3)  评论(0编辑  收藏  举报