zoukankan      html  css  js  c++  java
  • mybatis源码分析(6)-----核心调度对象StatmentHandler

    写在前面

      通过上一偏文章,我们知道:mybatis 的插件开发,主要是集中在Executor(执行器),ParameterHandler(参数处理器),ResultSetHandler(结果集处理器),StatementHandler( 语句处理器)。可以称作myBatis核心的四大金刚

      我们知道了mybatis对外暴露的API(SqlSession)的操作,其实是其持有Executor对于底层的操作。也知道了Executor 的设计采用了模板方法模式。

      查看Executor 的源代码发现,它对底层的操作,其实是其持有StatmentHandler对于 底层的操作。

    public class SimpleExecutor extends BaseExecutor {
      @Override
      public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
        //更新方法,首先获取StatmentHandler StatementHandler handler
    = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); stmt = prepareStatement(handler, ms.getStatementLog());
    //对于update 方法,只会有parameterHandler 帮忙绑定预编译的sql的参数。①
    return handler.update(stmt); } finally { closeStatement(stmt); } } @Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration();
        //查询方法,首先获取StatmentHandler StatementHandler handler
    = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog());
    //对于query方法,parameterHandler 和 resultSetHandler 会前来帮绑定参数和结果集处理 ②
    return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog);
    //statmentHandler 预编译sql stmt
    = handler.prepare(connection);
    //statmentHandler 绑定参数 handler.parameterize(stmt);
    return stmt; } }

      首先说明本文重点:

      文中将的四大对象是指:executor, statementHandler,parameterHandler,resultSetHandler对象

      讲到statementHandler,毫无疑问它是我们四大对象最重要的一个,它的任务就是和数据库对话。在它这里会使用parameterHandler和ResultSetHandler对象为我们绑定SQL参数和组装最后的结果返回。

    StatmentHandler 接口

      statmentHandler 位于mybatis的org.apache.ibatis.executor.statement 包下面。也可以发现其和executor的关系

    public interface StatementHandler {
      //预编译
      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;
    
      BoundSql getBoundSql();
      ParameterHandler getParameterHandler();
    }

    通过对stantmentHandler 接口的结构分析。它和executor的设计几乎一模一样,我可以猜测statmentHandler 也是采用了模板方法模式的设计。

     StatmentHandler 的产生

      通过对于Executor 的分析,可以发现executor 产生了一个RoutingStatmentHandler (从名字中可以发现,这是一个路由)

      这里有几个重要的方法,prepare,parameterize和query,update,他们的作用是不一样的。

      在MyBatis实现了statementHandler的有四个类:

      RoutingStatementHandler,这是一个封装类,它不提供具体的实现,只是根据Executor的类型,创建不同的类型StatementHandler。

      SimpleStatementHandler,这个类对应于JDBC的Statement对象,用于没有预编译参数的SQL的运行。

      PreparedStatementHandler 这个用于预编译参数SQL的运行。

      CallableStatementHandler 它将实存储过程的调度。

      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        //statmentHandler 的产生
    StatementHandler statementHandler
    = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }

       根据不同的statatmentType 产生具体的statmentHandler实例

      public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        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中,Configuration对象会采用new RoutingStatementHandler()来生成StatementHandler对象,换句话说我们真正使用的是RoutingStatementHandler对象,然后它会根据Executor的类型去创建对应具体的statementHandler对象(SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler)。

      然后利用具体statementHandler的方法完成所需要的功能。那么这个具体的statementHandler是保存在RoutingStatementHandler对象的delegate属性的,所以当我们拦截statementHandler的时候就要常常访问它了。

    statmentHandler 的执行

      prepare方法:首先prepare方法是用来编译SQL的,让我们看看它的源码实现。这里我们看到了BaseStatementHandler对prepare方法的实现,

      parameterize方法:上面我们在prepare方法里面预编译了SQL。那么我们这个时候希望设置参数。在Statement中我们是使用parameterize方法进行设置参数的。

      让我们看看PreparedStatementHandler中的parameterize方法:

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

      很显然这里很简单是通过parameterHandler来实现的,我们这篇文章只是停留在statementhandler的程度,等我们讲解parameterHandler的时候再来看它如何实现吧,期待一下吧。

      也就是不管是执行,query和update。parameterHandler 都会前来帮忙,帮助参数的绑定。参见①

          对于执行query方法。ResultSetHandler才会前来帮忙,对结果集做处理。参见②

      query/update方法

      我们用了prepare方法预编译了SQL,用了parameterize方法设置参数,那么我们接下来肯定是想执行SQL,而SQL无非是两种:
        一种是进行查询——query,另外就是更新——update。这些方法都很简单,让我们看看PreparedStatementHandler的实现:

      @Override
      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;
      }
    
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.<E> handleResultSets(ps);
      }

      我们可以看到如果是进行update的,它将会执行生成主键的操作(插入数据要自动生成主键的时候),然后就返回影响行数。
      如果是进行query的就更加简单了,它就是执行SQL语句,然后讲结果使用resultSetHandler去完成我们的结果组装。

        

    parameterhandler 和 ResultSetHandler

      parameterhandler 和 ResultSetHandler好比StatmentHandler 的左膀右臂。先后完成了参数绑定设置,已经结果集设置。他们的默认实现了是DefaultParameterHandler和DefaultResultSetHandler

      在构造statmentHandler 实例的时候,会同时产生parameterhandler 和 ResultSetHandler实例。并持有他们的对象。

    public abstract class BaseStatementHandler implements StatementHandler {
    
      protected final Configuration configuration;
      protected final ObjectFactory objectFactory;
      protected final TypeHandlerRegistry typeHandlerRegistry;
      protected final ResultSetHandler resultSetHandler;
      protected final ParameterHandler parameterHandler;
    
      protected final Executor executor;
      protected final MappedStatement mappedStatement;
      protected final RowBounds rowBounds;
    
      protected BoundSql boundSql;
    
      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;
    
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();
    
        if (boundSql == null) { // issue #435, get the key before calculating the statement
          generateKeys(parameterObject);
          boundSql = mappedStatement.getBoundSql(parameterObject);
        }
    
        this.boundSql = boundSql;
    
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
      }
    }

    ParameterHandler 的作用  

      我们知道ParameterHandler是用来设置参数规则的。档StatementHandler使用prepare()方法后,接下来就是使用它来设置参数,让我们看看它的定义:

    public interface ParameterHandler {
      Object getParameterObject();
      void setParameters(PreparedStatement ps) throws SQLException;
    }

      十分简单getParameterObject()是获取参数的,而setParameters()是设置参数的,相当于对一条sql所有的参数都执行ps.setXXX(value);

       让我们看看它的实现类:DefaultParameterHandler中的方法实现

      @Override
      public void setParameters(PreparedStatement ps) {
        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())) {
                value = parameterObject;
              } else {
                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 {
                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);
              }
            }
          }
        }
      }

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

    ResultSetHandler

      ResultSetHandler要比ParameterHandler复杂得多,对其原理要讲清楚真的很困难,但是我们一般在插件里面使用不多。所以我就不分析太详细了,大致讲讲它的大概原理。让我们看看ResultSetHandler的接口定义:

    public interface ResultSetHandler {
      <E> List<E> handleResultSets(Statement stmt) throws SQLException;
      void handleOutputParameters(CallableStatement cs) throws SQLException;
    
    }

    总结

      通过上面几篇我们懂得了SqlSession下得【四大对象】Executor,StatementHandler,ParameterHandler 和 ResultSetHandler之间的主要方法和协作。

      因为掌握好四大对象的基本方法,了解他们的作用是我们插件编写的根本,没有掌握它们你是没有办法准确的去编写你的插件的。

      StatementHandler是MyBatis四大对象里面最重要的对象,它的方法是十分重要的,也是我们插件的基础。

      当我们需要改变sql的时候,显然我们要在预编译SQL(prepare方法前加入修改的逻辑)。
      当我们需要修改参数的时候我们可以在调用parameterize方法前修改逻辑。或者使用ParameterHandler来改造设置参数。
      我们需要控制组装结果集的时候,也可以在query方法前后加入逻辑,或者使用ResultSetHandler来改造组装结果。
      懂的这些方法,才能理解我需要拦截什么对象,如何处理插件,这是MyBatis的核心内容。

  • 相关阅读:
    ListView Item 点击展开隐藏问题
    01背包基础 (杭电2602)
    实现一个简单的语音聊天室(多人语音聊天系统)
    Tomcat全攻略
    Linux pipe函数
    从网页抓取数据的一般方法
    华为的面试经历
    微软2014校园招聘笔试试题
    C++ 虚函数表解析
    unity3d 改动gui label颜色,定义颜色需除以256
  • 原文地址:https://www.cnblogs.com/chihirotan/p/6684261.html
Copyright © 2011-2022 走看看