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

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 类型
BooleanTypeHandlerjava.lang.Boolean, boolean任何兼容的布尔值
ByteTypeHandlerjava.lang.Byte, byte任何兼容的数字或字节类型
ShortTypeHandlerjava.lang.Short, short任何兼容的数字或短整型
IntegerTypeHandlerjava.lang.Integer, int任何兼容的数字和整型
LongTypeHandlerjava.lang.Long, long任何兼容的数字或长整型
FloatTypeHandlerjava.lang.Float, float任何兼容的数字或单精度浮点型
DoubleTypeHandlerjava.lang.Double, double任何兼容的数字或双精度浮点型
BigDecimalTypeHandlerjava.math.BigDecimal任何兼容的数字或十进制小数类型
StringTypeHandlerjava.lang.StringCHAR 和 VARCHAR 类型
ClobTypeHandlerjava.lang.StringCLOB 和 LONGVARCHAR 类型
NStringTypeHandlerjava.lang.StringNVARCHAR 和 NCHAR 类型
NClobTypeHandlerjava.lang.StringNCLOB 类型
ByteArrayTypeHandlerbyte[]任何兼容的字节流类型
BlobTypeHandlerbyte[]BLOB 和 LONGVARBINARY 类型
DateTypeHandlerjava.util.DateTIMESTAMP 类型
DateOnlyTypeHandlerjava.util.DateDATE 类型
TimeOnlyTypeHandlerjava.util.DateTIME 类型
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP 类型
SqlDateTypeHandlerjava.sql.DateDATE 类型
SqlTimeTypeHandlerjava.sql.TimeTIME 类型
ObjectTypeHandlerAny其他或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR-任何兼容的字符串类型, 作为代码存储(而不是索引)
EnumOrdinalTypeHandlerEnumeration TypeAny 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的用法和作用。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值