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

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************
前言

难得复工了,公司百业待兴,有一大堆接口需要对接,忙的不行。回过神来发现自己快一个月没写博客了,赶紧抽时间写一写,不能断更。

截止上一篇博客,我们已经把结果集映射的内容介绍完毕,接下来就是对Mybatis中的核心接口进行介绍,通过介绍这些核心接口,使读者们更深刻地理解Mybatis的运行机制以及原理。
StatementHandler

StatementHandler接口是Mybatis的核心接口之一,它完成了Mybatis中最核心的工作,也是Executor接口实现的基础。

StatementHandler接口中功能有很多,如创建Statement对象、执行SQL语句、批量执行SQL语句等。StatementHandler接口定义如下。

/**

  • Mybatis核心接口之一,完成了Mybatis中最核心的工作,也是Executor接口实现的基础。

  • @author Clinton Begin
    */
    public interface StatementHandler {

    /**

    • 从数据库连接中获取一个Statement
    • @param connection
    • @param transactionTimeout
    • @return
    • @throws SQLException
      */
      Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

    /**

    • 绑定statement执行时所需的实参
    • @param statement
    • @throws SQLException
      */
      void parameterize(Statement statement)
      throws SQLException;

    /**

    • 批量执行sql语句
    • @param statement
    • @throws SQLException
      */
      void batch(Statement statement)
      throws SQLException;

    /**

    • 执行 update/insert/delete语句
    • @param statement
    • @return
    • @throws SQLException
      */
      int update(Statement statement)
      throws SQLException;

    /**

    • 执行select语句
    • @param statement
    • @param resultHandler
    • @param
    • @return
    • @throws SQLException
      */
      List query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

    /**

    • 查询游标
    • @param statement
    • @param
    • @return
    • @throws SQLException
      */
      Cursor queryCursor(Statement statement)
      throws SQLException;

    /**

    • 获取BoundSql
    • @return
      */
      BoundSql getBoundSql();

    /**

    • 获取ParameterHandler
    • @return
      */
      ParameterHandler getParameterHandler();

}

该接口的继承关系如下。其中,CallableStatementHandler用于调用存储过程,而mysql的存储过程在大多数公司都很少使用甚至禁止使用,这里就不对其进行介绍了,有兴趣的朋友可以自己参考源码阅读

RoutingStatementHandler

RoutingStatementHandler使用策略模式,根据MappedStatement中指定的statementType字段,创建对应的StatementHandler接口实现。由于其这种思路,还有人认为这是个路由。RoutingStatementHandler核心代码如下。

public class RoutingStatementHandler implements StatementHandler {

/*** 底层封装的真正的StatementHandler对象* 这里的delegate在写框架的过程中使用较多*/
private final StatementHandler delegate;public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// RoutingStatementHandler的主要功能是根据MappedStatement的配置,生成对应的StatementHandler对象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());}}

}

BaseStatementHandler

BaseStatementHandler是一个抽象类,实现了StatementHandler接口。它只提供了一些参数绑定相关的方法,对数据库不进行任何操作。该类的字段如下。

protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;/*** 记录结果集映射对象*/
protected final ResultSetHandler resultSetHandler;/*** 记录使用的参数处理器对象。ParameterHandler的主要功能是为SQL绑定实参* 也就是使用传入的实参替换SQL语句中的 ? 占位符*/
protected final ParameterHandler parameterHandler;protected final Executor executor;/*** 记录SQL语句对应的MappedStatement*/
protected final MappedStatement mappedStatement;/*** 记录用户设置的offset和limit,用于在结果集中定位映射的起始位置和结束位置*/
protected final RowBounds rowBounds;protected BoundSql boundSql;

在BaseStatementHandler的构造方法中,除对上面的字段进行初始化之外,还会调用KeyGenerator.processBefore()方法初始化SQL的主键,具体实现如下。

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) {// 调用KeyGenerator.processBefore 方法获取主键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);
}protected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();ErrorContext.instance().store();keyGenerator.processBefore(executor, mappedStatement, null, parameter);ErrorContext.instance().recall();
}

BaseStatementHandler实现了StatementHandler的prepare方法,该方法用来初始化Statement对象,然后为其分配超时时间等属性。其中,初始化StatementHandler的方法是个抽象方法instantiateStatement,是一个抽象方法,需要由子类去实现,代码如下。

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// 初始化statement对象,交给子类去实现statement = instantiateStatement(connection);// 设置超时时间setStatementTimeout(statement, transactionTimeout);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);}
}

BaseStatementHandler依赖两个重要的组件,分别是ParameterHandler和ResultSetHandler。后者在前面的文章中已经介绍,不在重复。
ParameterHandler

通过前面介绍动态SQL可知,在BoundSql中记录的SQL语句可能包含?占位符,每个?占位符都对应了BoundSql.parameterMapings集合中的一个元素。在ParameterHandler中只定义了一个setParameter方法,该方法用于为SQL语句绑定实参。ParameterHandler接口只有唯一一个实现类 DefaultParameterHandler,核心字段如下。

/*** 管理Mybatis中的全部TypeHandler对象*/
private final TypeHandlerRegistry typeHandlerRegistry;/*** SQL节点*/
private final MappedStatement mappedStatement;/*** 用户传入的实参对象*/
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;

DefaultParameterHandler的setParameters方法中会遍历parameterMappings集合中记录的ParameterMapping对象,并根据其中记录的参数名称找到对应的实参,再与SQL绑定。setParameters方法如下。

/*** 设置参数* @param ps*/
@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);// OUT是存储过程中的输出参数,这里需要过滤掉这些参数if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;// 获取参数名称String propertyName = parameterMapping.getProperty();// 获取对应的实参值if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {// 实参可以直接通过TypeHandler转换成jdbcTypevalue = parameterObject;} else {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.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了。
SimpleStatementHandler

SimpleStatementHandler是BaseStatementHandler的子类,底层使用了Statement对象完成数据库的相关操作,所以SQL语句中不能存在占位符,因此parameterize方法是空实现。

SimpleStatementHandler的instantiateStatement方法直接通过JDBC Connection创建Statement,代码如下。

/*** 创建statement对象* @param connection* @return* @throws SQLException*/
@Override
protected 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);}
}

query方法完成了数据库查询的操作,并通过ResultSetHandler将结果集映射成结果对象,代码如下。

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.handleResultSets(statement);
}

update方法负责执行insert、update、delete的SQL语句,并根据配置的KeyGenerator获取数据库生成的主键,具体实现如下。

/*** 负责执行insert、update、delete语句* @param statement* @return* @throws SQLException*/
@Override
public int update(Statement statement) throws SQLException {String sql = boundSql.getSql();Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();int rows;if (keyGenerator instanceof Jdbc3KeyGenerator) {statement.execute(sql, Statement.RETURN_GENERATED_KEYS);rows = statement.getUpdateCount();keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);} else if (keyGenerator instanceof SelectKeyGenerator) {statement.execute(sql);rows = statement.getUpdateCount();keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);} else {statement.execute(sql);rows = statement.getUpdateCount();}return rows;
}

PreparedStatementHandler

该类底层依赖于PrepareStatement对象完成数据库的操作,instantiateStatement方法直接调用Connection的prepareStatement方法创建PrepareStatement对象,代码如下。

/*** 直接调用Connection的prepareStatement方法创建PrepareStatement对象* @param connection* @return* @throws SQLException*/
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {// 获取待执行的sqlString sql = boundSql.getSql();// 根据keyGenerator的值创建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 {// 创建普通的PrepareStatement对象return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);}
}

PrepareStatement的其他方法的实现与SimpleStatementHandler对应的方法实现类型,这里就不赘述了。

*************************************优雅的分割线 **********************************

分享一波:程序员赚外快-必看的巅峰干货

如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程

请关注微信公众号:HB荷包
在这里插入图片描述
一个能让你学习技术和赚钱方法的公众号,持续更新
*************************************优雅的分割线 **********************************

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Shell学习之结合正则表达式与通配符的使用(五)

Shell学习之结合正则表达式与通配符的使用 目录 通配符 正则表达式与通配符通配符通配符的使用正则表达式 正则表达式正则表达式的使用通配符 正则表达式与通配符 正则表达式用来在文件中匹配符合条件的字符串&#xff0c;正则是包含匹配。grep、awk、sed等命令可以支持正则表达…

Mybatis源码阅读(四):核心接口4.2——Executor(上)

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

接收xml参数_SpringBoot实战(二):接收xml请求

强烈推荐一个大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在对接一个第三方系统&#xff0c;需要接收第三方系统的回调&#xff0c;而且格式为XML形式&#xff0c;之前自己一般接收的参数是Json形式&#xff0c;于是乎做个实验验证一下使用…

报错 插入更新_window如何解决mysql数据量过大导致的报错

window如何解决报错“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中输入mysql -hlocalhost -uroot -p #如果设置了密码直接接在p 后面 show variables like %storage_engine%以下为结果可以看到InnoDB是MySQL的默认引…

Mybatis源码阅读(四):核心接口4.2——Executor(下)

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Mybatis源码阅读(五 ):接口层——SqlSession

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

插入公式_一个小工具,彻底帮你搞定在Markdown中插入公式的问题

在编辑Markdown文档时&#xff0c;插入公式是一个挺麻烦的活儿。需要掌握LaTex语法。我自己看完语法后&#xff0c;直接放弃&#xff0c;这绝对是反人类的语法。&#xff08;好吧&#xff0c;是我不会用...&#xff09;但是&#xff0c;我相信你看了这篇文章后&#xff0c;绝对…

Mybatis源码阅读(一):Mybatis初始化1.2 —— 解析别名、插件、对象工厂、反射工具箱、环境

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Google 修改 Chrome API,防止隐身模式检测

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; 在使用 Chrome 浏览网页时&#xff0c;某些网站会使用某种方法来确定访问者是否处于隐身模式&#xff0c;这是一种隐私泄漏行为。Google 目前正在考虑修改 Chrome 的相关 API&#xff0c;来杜绝…

Mybatis源码阅读(一):Mybatis初始化1.1 解析properties、settings

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

JavaScript异步基础

唯一比不知道代码为什么崩溃更可怕的事情是&#xff0c;不知道为什么一开始它是工作的&#xff01;在 ECMA 规范的最近几次版本里不断有新成员加入&#xff0c;尤其在处理异步的问题上&#xff0c;更是不断推陈出新。然而&#xff0c;我们在享受便利的同时&#xff0c;也应该了…

Flutter、ReactNative、uniapp对比

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

多线程中ThreadLocal的使用

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

注解版poi操作工具

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

Kali Linux 2019.1 发布,Metasploit 更新到 5.0 版本

百度智能云 云生态狂欢季 热门云产品1折起>>> Kali Linux 2019.1 发布了&#xff0c;Kali 前身 BackTrack&#xff0c;它是一个基于 Debian 的 Linux 发行版&#xff0c;主要用于信息安全行业&#xff0c;其包含了一系列安全、渗透测试和取证工具。此版本 Linux 内核…

peewee mysql_scrapy中利用peewee插入Mysql

前两天老大布置一个任务&#xff0c;说爬下来的数据要存入数据库中&#xff0c;丢给我一个peewee&#xff0c;说用这个。当时的我两眼一抹黑&#xff0c;这是个什么东西呀&#xff0c;我知道scrapy的数据存入数据库是在pipelines中进行设置但是peewee是什么东西呢。经过两天不懈…

Java版数据结构与算法——线性表

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

基于 CODING 的 Spring Boot 持续集成项目

本文作者&#xff1a;CODING 用户 - 廖石荣 持续集成的概念 持续集成(Continuous integration,简称 CI&#xff09;是一种软件开发实践&#xff0c;即团队开发成员经常集成他们的工作&#xff0c;通常每个成员每天至少集成一次&#xff0c;也就意味着每天可能会发生多次集成。每…

Mybatis组成部分

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…

一年java工作经验-面试总结

*************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如果以上内容对你觉得有用,并想获取更多的赚钱方式和免费的技术教程 请关注微信公众号:HB荷包 一个能让你学习技术和赚钱方法的公众号,持续更…