StatementHandler接口是MyBatis核心接口之一,它完成了MyBatis最核心的工作。

StatementHandler接口中的功能很多,例如创建Statement对象,为SQL语句绑定实参,执行select、insert、update、delete等多种类型的SQL语句,批量执行SQL语句,将结果集映射成结果对象。

StatementHandler接口的定义如下:

public interface StatementHandler {// 从连接中获取一个StatementStatement prepare(Connection connection, Integer transactionTimeout)throws SQLException;// 参数绑定void parameterize(Statement statement)throws SQLException;// 批量执行void batch(Statement statement)throws SQLException;// 执行update、insert、delete操作int update(Statement statement)throws SQLException;// 执行select操作<E> List<E> query(Statement statement, ResultHandler resultHandler)throws SQLException;<E> Cursor<E> queryCursor(Statement statement)throws SQLException;BoundSql getBoundSql();ParameterHandler getParameterHandler();}

MyBatis中提供了StatementHandler接口的多种实现类。

1.RoutingStatementHandler

RoutingStatementHandler会根据MappedStatement指定的statementTyp字段,创建对应的StatementHandler接口实现。RoutingStatementHandler类的具体实现如下:

public class RoutingStatementHandler implements StatementHandler {// 封装的真正的StatementHandler对象private final StatementHandler delegate;public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// 根据MappedStatement的配置,生成一个对应的StatementHandler对象,并设置到delegate字段中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());}}//...通过delegate调用对应的方法
}
2.BaseStatementHandler

BaseStatementHandler是一个实现了StatementHandler接口的抽象类,它只提供了一些参数绑定相关的方法,并没有实现操作数据库的方法。BaseStatementHandler字段的含义如下:

 // 记录使用的ResultSetHandler对象,将结果集映射成结果对象protected final ResultSetHandler resultSetHandler;// 记录使用的ParameterHandler对象,使用传入的实参替换SQL语句的中的?占位符protected final ParameterHandler parameterHandler;// 记录执行SQL语句的Executor对象protected final Executor executor;// 记录SQL语句对应的MappedStatement、BoundSql对象protected final MappedStatement mappedStatement;// RowBounds记录了用户设置的offset,limit,用于在结果集中定位映射的起始位置和结束位置protected final RowBounds rowBounds;

在BaseStatementHandler的构造方 法中,除了初始化上述字段之外,还会调用KeyGenerator.processBefore()方法初始化SQL语句的主键,具体实现如下:

 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// ...其他字段忽略if (boundSql == null) { // issue #435, get the key before calculating the statement// 调用keyGenerator.processBefore()方法获取主键generateKeys(parameterObject);boundSql = mappedStatement.getBoundSql(parameterObject);}}protected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();ErrorContext.instance().store();keyGenerator.processBefore(executor, mappedStatement, null, parameter);ErrorContext.instance().recall();}

BaseStatementHandler实现了StatementHandler接口中的prepare()方法,该方法首先调instantiateStatement()抽象方法初始化Java.sqI.Statement对象 然后为其配置超时时间以及fetchSize等设置。

3.ParameterHandler

通过前面对动态SQL的介绍可知,在BoundSql中记录的SQL语句中可能包含?占位符,而每个?占位符都对应了BoundSql、parameterMappings集合中的一个元素,在该ParameterMapping中记录了对应参数名称以及该参数的相关属性。

在ParameterHandler接口中只定义了一个setParameters()方法,该方法主要负责调用PreparedStatement.set*()方法为SQL语句绑定实参。MyBatis只为ParameterHandler接口提供了一个实现类DefaultParameterHandler。DefaultParameterHandler中核心字段的含义如下:

  // 管理mybatis中的全部TypeHandler对象private final TypeHandlerRegistry typeHandlerRegistry;// 其中记录SQL节点相应的配置信息private final MappedStatement mappedStatement;// 用户传入的实参对象private final Object parameterObject;// 对应的BoundSql对象,需要设置参数的PreparedStatement对象private final BoundSql boundSql;

在DefaultParameterHandler.setParameters()方法中会遍历BoundSql.parameterMappings集合中记录的ParameterMapping对象,井根据其中记录的参数名称查找相应实参,然后与SQL语句绑定。setParameters()方法的具体实现如下:

 public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());// 取出sql中的参数映射列表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())) {// 根据TypeHandler转换成JdbcTypevalue = parameterObject;} else {// 获取对象中相应的属性位或查找Map对象中值MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// 获取ParameterMapping中设置的TypeHandler对象TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {// //通过TypeHandler.setParameters()方法会调用PreparedStatement.set*()方法为SQL语句绑定相应的实参typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}}

为SQL语句绑定完实参之后,就可以调用Statement对象相应的execute()方法,将SQL语句交给数据库执行。

4.SimpleStatementHandler

SimpleStatementHandler继承了BaseStatementHandler抽象类。它底层使用java.sql.Statement对象来完成数据库的相关操作,所以SQL语句中不能存在占位符相应的,SimpleStatementHandler.parameterize()方法是空实现。

SimpleStatementHandler.instantiateStatement()方法直接通过JDBCConnection 创建Statement对象,具体实现如下:

@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {return connection.createStatement();} else {// 设置结果集是否可以滚动及其游标是否可以上下移动,设置结果集是否可更新return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);}}

上面创建的Statement对象之后会被用于完成数据库操作,SimpleStatementHandler.query()方法等完成了数据库查询的操作,并通过ResultSetHandler将结果集映射成结果对象。

@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {// 获取SQL语句String sql = boundSql.getSql();// 执行SQL语句statement.execute(sql);//  映射结果集return resultSetHandler.handleResultSets(statement);}

SimpleStatementHandler中的 queryCursor()、batch()方法与query()方法实现类似,也是直接调用Statement对象的相应方法,不再赘述。

SimpleStatementHandler.update()方法负责执行insert、update、delete等类型的SQL语句,并且会根据配置的 KeyGenerator获取数据库生成的主键 具体实现如下:

 @Overridepublic int update(Statement statement) throws SQLException {// 获取SQL语句String sql = boundSql.getSql();// 获取实参对象Object parameterObject = boundSql.getParameterObject();// 获取配置的KeyGenerator对象KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();int rows;if (keyGenerator instanceof Jdbc3KeyGenerator) {// 执行SQL语句statement.execute(sql, Statement.RETURN_GENERATED_KEYS);// 获取受影响的行rows = statement.getUpdateCount();// 将数据库生成的主键添加到parameterObject中keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);} else if (keyGenerator instanceof SelectKeyGenerator) {// 执行SQL语句statement.execute(sql);// 获取受影响的行rows = statement.getUpdateCount();// 执行<selectKey>节点中配置的SQL语句获取数据库生成的主键,添加到parameterObject中keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);} else {statement.execute(sql);rows = statement.getUpdateCount();}return rows;}
5.PreparedStatementHandler

PreparedStatementHandler底层依赖于java.sql.PreparedStatement对象来完成数据库的相关操作。在 SimpleStatementHandler.parameterize()方法中, 会调用前面介绍的ParameterHandler.setParameters ()方法完成SQL语句的参数绑定。

PreparedStatementHandler.instantiateStatement()方法直接调用JDBC Connection的prepareStatement()方法创建 PreparedStatement对象, 具体实现如下:

@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {// 获取SQL语句String sql = boundSql.getSql();// 获取mappedStatement.getKeyGenerator()创建prepareStatement对象if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {String[] keyColumnNames = mappedStatement.getKeyColumns();if (keyColumnNames == null) {return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);} else {// insert语句执行完成之后,会将keyColumnNames指定的列返回return connection.prepareStatement(sql, keyColumnNames);}} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {return connection.prepareStatement(sql);} else {// 设置结果集是否可以滚动以及其游标是否可以上下移动,设置结果集是否可更新return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);}}

PreparedStatementHandler中其他方法的实现与SimpleStatementHandler对应方法的实现类似,这里就不再赘述。

6.CallableStatementHandler

CallableStatementHandler底层依赖于java.sql.CallableStatement存储过程parameterize()方法也会调用ParameterHandler.setParameters ()方法完成SQL语句的参数绑定指定输出参数的索引位置和JDBC类型。其余方法与前面介绍SimpleStatementHandler实现类似,唯一区别是会调用前面介绍的 resultSetHandler.handleOutputParameters()处理输出参数。

mybatis之StatementHandler相关推荐

  1. mybatis中statementHandler的设计与实现

    1.StatementHandler StatementHandler接口定义包含创建Statement, 绑定参数,批量执行,更新,查询,获取参数处理器 类结构图为 PreparedStatemen ...

  2. 4.MyBatis源码解析-MyBatis扩展点--阿呆中二

    MyBatis扩展点 MyBatis MyBatis扩展点 与我联系 MyBatis 本文是对mybatis 3.x源码深度解析与最佳实践学习的总结,包括XML文件解析流程.SqlSession构建流 ...

  3. 手写mybatis完整sql插件

    问题产生 我们在使用mybatis的过程中,如果开启了mysql的日志功能的话,会在控制台打印一些sql的信息,但是日志中的sql语句,是没有拼接参数的,也就是说,是不可以直接放到数据库中执行的. s ...

  4. mybatis拦截器开发-分表插件

    相关源码已上传至我的github,对应的插件代码在src/main/java/net/dwade/plugins/mybatis目录 https://github.com/huangxfchn/dwa ...

  5. MyBatis3源码解析(3)查询语句执行

    简介 上篇探索了MyBatis中如何获取数据库连接,本篇继续探索,来看看MyBatis中如何执行一条查询语句 测试代码 本篇文中用于调试的测试代码请参考:MyBatis3源码解析(1)探索准备 完整的 ...

  6. mybatis源码阅读(六) ---StatementHandler了解一下

    转载自  mybatis源码阅读(六) ---StatementHandler了解一下 StatementHandler类结构图与接口设计 BaseStatementHandler:一个抽象类,只是实 ...

  7. Mybatis源码阅读(四):核心接口4.1——StatementHandler

    *************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如 ...

  8. mybatis核心配置_MyBatis 核心配置综述之StatementHandler

    StatementHandler 是四大组件中最重要的一个对象,负责操作 Statement 对象与数据库进行交流,在工作时还会使用 ParameterHandler 和 ResultSetHandl ...

  9. Mybatis源码笔记之浅析StatementHandler

    目录 概述 职责 类图 源码 StatementHandler对象创建 总结 概述 职责 首先了解一下statementHandler职责:主要负责处理MyBatis与JDBC之间Statement的 ...

最新文章

  1. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试...
  2. 每日一题:leetcode173.二叉搜索树迭代器
  3. typescript在ES3(IE7)环境下使用async、await
  4. MySQL忘记root密码的解决办法
  5. web开发 开发一个能发送邮件的应用
  6. 投资五大基本法则,助你在理财投资路上走得更平稳顺利
  7. 企业信息安全建设要点梳理
  8. C++ int与string的相互转换
  9. Command “python setup.py egg_info“ failed with error code 1 in C:\Users\始末\AppData\Local\Temp\pip-b
  10. [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
  11. acr122 java,ACR122U中文开发文档
  12. photoshop 插件_Photoshop的线性光
  13. 标书怎么做?标书制作教程附标书制作思维导图
  14. PowerVR 6系列架构分析
  15. 听吐的微信提示音终于能改了
  16. C++ 学习记录(18) NVI
  17. 守护绿水青山,环保数采仪一马当先!
  18. CPU与RISC-V ISA架构
  19. 分析在线直播答题应用基础架构
  20. HDU4544 湫湫系列故事——消灭兔子

热门文章

  1. 一大波年终工作总结PPT来袭赶紧收藏!
  2. 大数据可视化——在组件中使用翻牌器
  3. 如何利用批处理文件快速打开截图软件呢?
  4. Aultium Designer 的使用心得和基本电路图的搭建
  5. 关于如何找到人生目标
  6. 华为设备配置组播静态路由改变RPF路由
  7. 对信道估计和信道均衡的理解
  8. 电脑反复出现蓝屏重启现象
  9. js加密怎样不让别人看到
  10. html选择文件用按钮,html点击button弹出挑选文件,上传,这个怎么实现?