ParameterHandler

ParameterHandler介绍

ParameterHandler是用来设置参数规则的。当StatementHandler使用prepare()方法后,接下来就是使用它来设置参数。

public interface ParameterHandler {

  Object getParameterObject();

  void setParameters(PreparedStatement ps)
      throws SQLException;

}

这个接口只定义了两个方法,其中setParameters方法是设置参数的,getParameterObject是获取参数的。

而且这个接口目前只有一个实现类DefaultParameterHandler。

setParameters方法的具体实现

我们接着下一篇分析的代码

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
  //得到JDBC Connection
    Connection connection = getConnection(statementLog);
  //这里就要到了上面说到的StatementHandler的一个重要方法perpare方法,用来预编译sql
  stmt = handler.prepare(connection);
  //调用了paramterize方法
    handler.parameterize(stmt);
    return stmt;
  }


  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }

这里的delegate属性是PrepareStatementHandler

所以调用的是

  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

parameterHandler在初始化PrepareStatementHandler对象时就在调用其父类BaseStatementHandler构造方法时就已经初始化了,对应一个DefaultParameterHandler实例。

好了,我们终于可以好好分析一下setParameters方法了

 public void setParameters(PreparedStatement ps) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
   // parameterMappings  是对#{} 里给出的参数信息的封装,即这个SQL是个参数化SQL(SQL语句中带占位符?)时会存在
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
   //如果是参数化SQL,便要设置参数
   if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        //如果参数的类型不是OUT类型,CallableStatementHandler会用到
        //因为存储过程才存在输出参数,所以当参数不是输出参数的时候,就需要设置
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          //在本次查询中,对应的不是一个Bean,对应的是一个additionalParameters Map对象,所以这里的property不是Bean的属性,而是经过封装的Key(可以通过Key找到对应的Value,该Value就是我们要找的值)
         //如果是Bean 则通过属性的反射来得到Value
          String propertyName = parameterMapping.getProperty();
          //如果propertyName是additionalParameters中的key
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            //通过Key来得到Map中的Value
            value = boundSql.getAdditionalParameter(propertyName);
          }//如果不是additionalParameters中的Key,而且传入参数是null,那value就为null
          else if (parameterObject == null) {
            value = null;
          } 
          //如果在typeHandlerRegistry中已经注册了这个参数的Class对象,即他是Primitive类型或者是String ByteArray 等等 26个类型 也就是说不是JavaBean 也不是Map List类型时。value直接等于Method传进来的参数
          else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          }
          //否则就是Map(Method穿进行的原始参数是List类型或者是Array类型时,这时已经被封装成了Map类型了)类型或者是Bean 通过封装的MataObject对象,用propertyName得到相应的Value,Bean通过反射得到,Map类型则是通过Key得到Value
          else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          //在通过SqlSource的parse方法得到paramterMappings的具体实现中,我们会得到parameterMapping的TypeHandler
        //本次查询中TypeHandler均为IntegerTypeHandler          
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          //得到相应parameterMapping的JdbcType,如果没有在#{}中显式的指定JdbcType,则为null
          JdbcType jdbcType = parameterMapping.getJdbcType();
          //如果得到的value为null并且jdbcType也是null的时候,jdbcType就会成为configuration.getJdbcTypeForNull();  即OTHER,所以我们在编写Mapper配置文件的Insert或者Update类型的SqlMapper时,如果某个#{propertyName}对应的Value是null的话,插入数据库是会报错的,
// 因为此时的JdbcType是Other,执行setNull时会报错,

       //java.sql.SQLException: 无效的列类型: 1111 
          //1111 就是OTHER对应的sqlType
         // 所以Value为null的情况下,我们需要指定JdbcType
          if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
          //这一行实现了具体的ps.set***
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        }
      }
    }
  }

daga

 typeHandler.setParameter(ps, i + 1, value, jdbcType);

BaseTypeHandler是IntegerTypeHandler和其他TypeHandler的父类

其中setParameter就是在BaseTypeHandler中实现的,这个设计依然采用了模板设计模式

public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
  //当传入的参数是null时
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        //执行PreparedStatement的setNull方法
        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 {
      //如果value不是null,则进行参数设置,这是个抽象方法,具体的实现在子类中
      setNonNullParameter(ps, i, parameter, jdbcType);
    }
  }

ps.setNull

setNull
void setNull(int parameterIndex,
             int sqlType)
             throws SQLException将指定参数设置为 SQL NULL。 
注:必须指定参数的 SQL 类型。 


参数:
parameterIndex - 第一个参数是 1,第二个参数是 2,……
sqlType - java.sql.Types 中定义的 SQL 类型代码 
抛出: 
SQLException - 如果 parameterIndex 不对应于 SQL 语句中的参数标记;如果发生数据库访问错误,或者在关闭的 PreparedStatement 上调用此方法 
SQLFeatureNotSupportedException - 如果 sqlType 是 ARRAY、BLOB、CLOB、DATALINK、JAVA_OBJECT、NCHAR、NCLOB、NVARCHAR、LONGNVARCHAR、REF、ROWID、SQLXML 或 STRUCT 数据类型之一并且 JDBC 驱动程序不支持此数据类型

当SQL类型代码为1111(OTHER)时,就会出现报错:java.sql.SQLException: 无效的列类型: 1111

此时就需要在#{}设置JdbcType。
IntegerTypeHandler中的setNonNullParameter方法具体实现,很简单

@Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值