Mybatis源码分析-StatementHandler

承接前文Mybatis源码分析-BaseExecutor,本文则对通过StatementHandler接口完成数据库的CRUD操作作简单的分析

StatementHandler#接口列表

  //获取Statement对象,供操作数据库
  Statement prepare(Connection connection)
      throws SQLException;

  //参数配置
  void parameterize(Statement statement)
      throws SQLException;

  //批处理
  void batch(Statement statement)
      throws SQLException;

  //更新操作
  int update(Statement statement)
      throws SQLException;

  //查询操作
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  //获取绑定的sql信息,包含sql语句
  BoundSql getBoundSql();
  
  //获取参数处理类,处理参数的映射等
  ParameterHandler getParameterHandler();

RoutingStatementHandler-代理委托类

其基本是间接类,只是根据statementType类型创建不同的StatementHandler类,达到适配的效果。此处我们只需要观察它的构造函数

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
	//根据mappedStatement中的statementType类型来进行委托类的创建
	//默认都是PrepareStatementHandler
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

mybatis在某语句不指定statementType属性的情况下,默认创建PrepareStatementHandler

BaseStatementHandler-基本抽象实现类

BaseStatementHandler-构造函数

瞧下代码

  protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;
	//java类型处理类
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();
	//此处兼容doUpdate()方法的调用,因为传过来的boundSql为null
    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;
	//paramterMap/resultType属性对应的入参集合处理
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    //resultMap/resultType属性对应的出参集合处理
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

BaseStatementHandler#prepare()-创建sql表达式对象statement

具体源码如下

  public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
	  //创建表达式对象,供子类复写
      statement = instantiateStatement(connection);
      //设置执行sql的超时时间
      setStatementTimeout(statement);
      //通过mappedStatement对象设置statement的fetchSize大小
      setFetchSize(statement);
      return statement;
    }//出现异常则关闭表达式对象 
    catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

此处我们只需要关注子类对instantiateStatement()方法的复写,其余的是设置执行sql超时时间、设置statement的fetchSize大小

我们以mybatis默认的PrepareStatementHandler为例

PrepareStatementHandler-预表达式帮助类

PrepareStatementHandler#instantiateStatement()-创建Statement对象

创建的对象为PrepareStatement,具体源码如下

  protected Statement instantiateStatement(Connection connection) throws SQLException {
    //获取待执行的sql语句
    String sql = boundSql.getSql();
    //主键列设置,一般此处针对insert语句
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    }//ResultSet返回类型,默认为空 
    else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

PrepareStatementHandler#update()-CUD操作入口

源码奉上

  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    //对增删改都会返回执行成功的记录数目
    int rows = ps.getUpdateCount();
    //针对key进行处理
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

PrepareStatementHandler#query()-R操作入口

源码奉上

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    //对select语句返回的resultMap/resultType进行相应的映射
    return resultSetHandler.<E> handleResultSets(ps);
  }

小结

  • mybatis对jdbc调用数据库的方式进行了封装,实质都是调用Statement对象来执行sql语句,掌握纯jdbc访问数据库是最基本的

  • MappedStatement对象包含的内容很多,用于数据库访问的有statementType、keyGenerator、resultSetType等属性,具体的读者可自行查阅XMLStatementBuilder#parseStatementNode()源码

posted @ 2017-08-15 17:34  南柯问天  阅读(496)  评论(0编辑  收藏  举报