Mybatis源码--ParameterHandler源码分析

1 概述

通过前面文章(Mybatis源码分析--StatementHandler源码分析 )的学习,我们已经知道了ParameterHandler是用于绑定参数的。通过查看ParameterHandler的源码,我们知道ParameterHandler是一个接口,而且找个接口仅仅有DefaultParameterHandler一个实现类。我们这里就来看看DefaultParameterHandler的实现。

2 DefaultParameterHandler

这里仅仅看一下比较重要的函数setParameters。

    @Override
    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

        //获取到sql语句对应的参数集合
        List parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {

            //遍历参数集合	
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);

                //仅仅处理输入参数
                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是Mybatis提供的一个用于方便、优雅访问对象属性的对象
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = configuration.getJdbcTypeForNull();
                    }
                    try {

                        //设置参数,这里可以猜想到最终还是调用的PreparedStatement的方法来进行参数设置  
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (TypeException e) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                    } catch (SQLException e) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                    }
                }
            }
        }
    }

首先它读取了ParameterObject参数对象,然后用typeHandler对参数进行设置,而typeHandler里面需要对jdbcType和javaType进行处理,然后就设置参数了。所以当我们使用TypeHandler的时候完全可以控制如何设置SQL参数。

我们来看一下TypeHandler的setParameter函数。

  @Override
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {

    //针对参数为空的处理
    if (parameter == null) {

      //如果没有对应的jdbcType则直接抛出异常
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {

        //调用PreparedStatement的设置空参数函数
        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 {

        //设置非空参数
        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);
      }
    }
  }

关于上面的函数,我们先来看一下jdbcType枚举类。

public enum JdbcType {
  /*
   * This is added to enable basic support for the
   * ARRAY data type - but a custom type handler is still required
   */
  ARRAY(Types.ARRAY),
  BIT(Types.BIT),
  TINYINT(Types.TINYINT),
  SMALLINT(Types.SMALLINT),
  INTEGER(Types.INTEGER),
  BIGINT(Types.BIGINT),
  FLOAT(Types.FLOAT),
  REAL(Types.REAL),
  DOUBLE(Types.DOUBLE),
  NUMERIC(Types.NUMERIC),
  DECIMAL(Types.DECIMAL),
  CHAR(Types.CHAR),
  VARCHAR(Types.VARCHAR),
  LONGVARCHAR(Types.LONGVARCHAR),
  DATE(Types.DATE),
  TIME(Types.TIME),
  TIMESTAMP(Types.TIMESTAMP),
  BINARY(Types.BINARY),
  VARBINARY(Types.VARBINARY),
  LONGVARBINARY(Types.LONGVARBINARY),
  NULL(Types.NULL),
  OTHER(Types.OTHER),
  BLOB(Types.BLOB),
  CLOB(Types.CLOB),
  BOOLEAN(Types.BOOLEAN),
  CURSOR(-10), // Oracle
  UNDEFINED(Integer.MIN_VALUE + 1000),
  NVARCHAR(Types.NVARCHAR), // JDK6
  NCHAR(Types.NCHAR), // JDK6
  NCLOB(Types.NCLOB), // JDK6
  STRUCT(Types.STRUCT);

  public final int TYPE_CODE;
  private static Map codeLookup = new HashMap();

  static {
    for (JdbcType type : JdbcType.values()) {
      codeLookup.put(type.TYPE_CODE, type);
    }
  }

  JdbcType(int code) {
    this.TYPE_CODE = code;
  }

  public static JdbcType forCode(int code)  {
    return codeLookup.get(code);
  }

}

从上面我们可以看出我们平时写mapper文件的时候,里面的jdbcType就是从这里来的。

setNonNullParameter函数是抽象类BaseTypeHandler的一个抽象函数。

请看下图:

Mybatis源码--ParameterHandler源码分析_第1张图片

我们可以发现setNonNullParameter函数有很多具体的实现,这些实现其实就是根据参数类型的不同来进行不同的PreparedStatement参数设置。

至此我们完成了ParameterHandler的源码分析,接下来我们将继续分析ResultSetHandler的源码,欢迎交流。

你可能感兴趣的:(MyBatis,Mybatis源码分析)