模板方法模式的应用之 MyBatis BaseTypeHandler

在 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 类中通用的步骤,既易于扩展,又易于维护。

posted @ 2025-02-18 23:27  Higurashi-kagome  阅读(61)  评论(0)    收藏  举报