MyBatis主流程分析之(三)-准备SQL语句和参数替换、执行

  • Post author:
  • Post category:其他




MyBatis主流程分析之(二)-打开会话和数据库操作

中我们只讲了一个主流程,没有深入了解mybatis是如何准备sql语句,如何替换参数,最后查询,新增和删除数据的。这里再补充一下,深入了解。

一、JDBC方式新增数据

首先,我们看看JDBC是如何实现的,无论mybatis内部怎么实现,肯定还是调用JDBC的。

这里可以参考

JDBC-基础

//sql语句?的地方就是PreparedStatement后面要替换的地方
 String sql = "insert into user values(?,?)";      
 PreparedStatement  pst = conn.prepareStatement(sql);            
 for(int i = 101;i<200;i++){
          //根据位置设置值
          pst.setString(1,"Tom" + i); 
          pst.setString(2,(100+i)*10);                                
          pst.executeUpdate();
 }

在这里PreparedStatement准备了SQL语句,根据位置设置了参数,最后调用executeUpdate。那么我们看看MyBatis是如何做到的。

二、mybatis的PreparedStatement的SQL语句准备

这个章节主要解释mybatis如何实现JDBC的PreparedStatement pst = conn.prepareStatement(sql);

在我们创建StatementHandler的实现类时候,它的基类BaseStatementHandler实现了上面的功能,见源代码

BaseStatementHandler类

  //代码有删减
 public Statement prepare(Connection connection) throws SQLException {
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);//实现的主要地方
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
    }
  }

  protected Statement instantiateStatement(Connection connection) throws SQLException {
    //这个boundSql,在我们创建StatementHandler的实现类的时候就已经创建。这是一个比较主要的类,我们在最后将化点时间讲解这个类。
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      .....
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      //这里就是JDBC的conn.prepareStatement(sql);
      return connection.prepareStatement(sql);
    }
  }

三、mybatis的PreparedStatement的参数设置

这个章节主要解释了JDBC的 pst.setString(1,”Tom” + i);

在DefaultParameterHandler类中(handler.parameterize(stmt)方法中调用)

 public void setParameters(PreparedStatement ps) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    //获取所有参数
    List<ParameterMapping> 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)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          //typeHandlerRegistry注册了某个类的处理
            value = parameterObject;
          } else {
            //默认的MetaObject 的处理,根据参数获取值,这个将和boundSql一起解释
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          //参数列的TypeHandler 
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          //jdbcType的处理
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
          //见下面的分析,这里实现了 JDBC的 pst.setString(1,"Tom" + i); 
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        }
      }
    }
  }

类型处理器的作用就是

– 查询时把数据库存储的值转换成java类型

– 修改是把java类型转换成数据库类型存储,处理

– 下面这个表格描述了默认的类型处理器。

类型处理器 Java 类型 JDBC 类型
BooleanTypeHandler java.lang.Boolean, boolean 任何兼容的布尔值
ByteTypeHandler java.lang.Byte, byte 任何兼容的数字或字节类型
ShortTypeHandler java.lang.Short, short 任何兼容的数字或短整型
IntegerTypeHandler java.lang.Integer, int 任何兼容的数字和整型
LongTypeHandler java.lang.Long, long 任何兼容的数字或长整型
FloatTypeHandler java.lang.Float, float 任何兼容的数字或单精度浮点型
DoubleTypeHandler java.lang.Double, double 任何兼容的数字或双精度浮点型
BigDecimalTypeHandler java.math.BigDecimal 任何兼容的数字或十进制小数类型
StringTypeHandler java.lang.String CHAR 和 VARCHAR 类型
ClobTypeHandler java.lang.String CLOB 和 LONGVARCHAR 类型
NStringTypeHandler java.lang.String NVARCHAR 和 NCHAR 类型
NClobTypeHandler java.lang.String NCLOB 类型
ByteArrayTypeHandler byte[] 任何兼容的字节流类型
BlobTypeHandler byte[] BLOB 和 LONGVARBINARY 类型
DateTypeHandler java.util.Date TIMESTAMP 类型
DateOnlyTypeHandler java.util.Date DATE 类型
TimeOnlyTypeHandler java.util.Date TIME 类型
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP 类型
SqlDateTypeHandler java.sql.Date DATE 类型
SqlTimeTypeHandler java.sql.Time TIME 类型
ObjectTypeHandler Any 其他或未指定类型
EnumTypeHandler Enumeration Type VARCHAR-任何兼容的字符串类型, 作为代码存储(而不是索引)
EnumOrdinalTypeHandler Enumeration Type Any compatible NUMERIC or DOUBLE, as the position is stored (not the code itself).



TypeHandle的接口

这里写图片描述

  • BaseTypeHandler类
  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 {
      //调用实现类
      setNonNullParameter(ps, i, parameter, jdbcType);
    }
  }
  • 其中StringTypeHandler的实现
  //设置第i的值
 @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setString(i, parameter);
  }

   //根据columnName获取值
  @Override
  public String getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getString(columnName);
  }
   //根据columnIndex获取值
  @Override
  public String getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getString(columnIndex);
  }
  //返回的是CallableStatement 获取值
  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getString(columnIndex);
  }

四、mybatis的PreparedStatement的executeUpdate

这里解释了mybatis如何实现JDBC的pst.executeUpdate();

在PreparedStatementHandler的实现类中

  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //执行语句
    ps.execute();
    //获取影响的函数
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

五、BoundSql类

BoundSql的作用

1. 存储我们的sql语

2. 存储传入参数对象

3. 利用MetaObjec对象获取或设置2(存储传入参数对象)中的值

这里写图片描述

  public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<String, Object>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }

主要属性

– sql

update mybatis.user set UserName=?,
                        UserEmail=?
              where userId= ?
  • parameterMappings

parameterMappings内的parameterMapping分别为UserName,UserEmail和userId对象。和参数顺序一直。

这里写图片描述

  • parameterObject

    例如一个pojo对象,User对象类。

  • additionalParameters

    空new HashMap

  • metaParameters

    MetaObjec的一个变量,见下文。

六、MetaObject类

先看看 MetaObject、ObjectWrapper和ObjectFactory、ObjectWrapperFactory的关系。

这里写图片描述

MetaObject的属性及其方法

  private Object originalObject;//例如一个pojo对象,例如User对象类。
  private ObjectWrapper objectWrapper;//根据参数对象的不同,在构造函数中配置
  private ObjectFactory objectFactory;//Configuration中配置
  private ObjectWrapperFactory objectWrapperFactory;//Configuration中配置

  //构造函数,私有
   private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;

    if (object instanceof ObjectWrapper) {
       //如果参数对象实现了ObjectWrapper
      this.objectWrapper = (ObjectWrapper) object;
    } else if (objectWrapperFactory.hasWrapperFor(object)) {
      //如果objectWrapperFactory已经包装了对象,对用objectWrapperFactory的getWrapperFor
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    } else if (object instanceof Map) {
      //是一个Map对象,使用mybatis的MapWrapper
      this.objectWrapper = new MapWrapper(this, (Map) object);
    } else if (object instanceof Collection) {
      //是一个CollectionWrapper对象
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {
      //其他默认使用BeanWrapper
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }

 //对外公开的静态方法
  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    if (object == null) {
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory);
    }
  }

主要的方法

这里写图片描述

通过这个MetaObject对象可以很方便获取或设置originalObject对象(传入对象参数)的值。

很有趣的是mybatis获取数据配合也调用了这个类,包装的对象是PooledDataSource。

这里解释了

MyBatis主流程分析之(一)-环境准备

中的ObjectFactory、ObjectWrapperFactory的用法和作用。



版权声明:本文为likewindy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。