博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis源码分析(6)-----核心调度对象StatmentHandler
阅读量:4991 次
发布时间:2019-06-12

本文共 11298 字,大约阅读时间需要 37 分钟。

写在前面

  通过上一偏文章,我们知道: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 
List
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.
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;  //查询操作  
List
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 
List
query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.
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
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 {  
List
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的核心内容。

转载于:https://www.cnblogs.com/chihirotan/p/6684261.html

你可能感兴趣的文章
架构师速成6.18-初中书单资料推荐
查看>>
linux系统的安装
查看>>
Java设计模式菜鸟系列(十三)建模和实现状态模式
查看>>
《Hadoop》对于高级编程Hadoop实现构建企业级安全解决方案
查看>>
android ndk通过遍历和删除文件
查看>>
Notification(一个)——使用演示样本的基础知识
查看>>
《算法导论》为什么经典
查看>>
windows如何能在“运行”框输入名称就启动相应的软件
查看>>
修复反编译资源文件及批量修复程序源码
查看>>
CODEVS 1217 借教室
查看>>
VM ware 安装时候的一些坑和解决办法
查看>>
【原】最长上升子序列——动态规划
查看>>
26. Remove Duplicates from Sorted Array
查看>>
RN开发-Navigator
查看>>
innodb二进制文件相关的参数
查看>>
前谷歌高管给初入职场新人的14条忠告
查看>>
01-html介绍和head标签
查看>>
Python之Linux下的 virtualenv
查看>>
ASP.NET Web开发框架之三 报表开发
查看>>
大家好
查看>>