MyBatis 之 StatementHandler 来执行 SQL 语句

简介

使用原生的 JDBC API 来执行 SQL,需要经历加、连、语、执、释步骤。如下:


private static void query() throws Exception {
     // 加载驱动(可以不显示加载,因为引入 MySQL 驱动之后会通过 SPI自动加载)
     Class.forName("com.mysql.jdbc.Driver");
     // 获取连接
     Connection connection = DriverManager.getConnection("jdbc:mysql:///test", "root", "admin");
     // 预编译语句
     PreparedStatement statement = connection.prepareStatement("SELECT * FROM user WHERE id = ?");
     // 设置参数
     statement.setLong(1, 1);
     // 执行 SQL
     ResultSet resultSet = statement.executeQuery();
     ResultSetMetaData metaData = resultSet.getMetaData();
     int columnCount = metaData.getColumnCount();
     while (resultSet.next()) {
         for (int i = 1; i <= columnCount; i++) {
             String columnName = metaData.getColumnName(i);
             String columnValue = resultSet.getString(columnName);
             System.out.println(columnName + " : " + columnValue);
         }
     }
     // 关闭
     statement.close();
     connection.close();
 }

在使用 MyBatis 框架的时候我们无感知,并没有如上一系列操作。只需要编写 Mapper.xml 中的 SQL 语句即可。在前面我们已经学习了 SQL 语句抽象根据实参获取 SQL 语句 ,现在我们学习在获取 SQL 之后用 StatementHandler 来处理 。

类图

在这里插入图片描述

  • RoutingStatementHandler:如其类名具有路由功能,MyBatis 框架内部根据 MappedStatement 中的 StatementType 类型来获取对应的 PreparedStatementHandler、CallableStatementHandler 和 SimpleStatementHandler 实例。
  • BaseStatementHandler:实现了 prepare 方法通用逻辑,通过定义 instantiateStatement 模板方法以获得不同的 Statement 实例并设置超时时间和从数据库获取数据数量。
  • SimpleStatementHandler:对应 JDBC Statement(静态 SQL 语句)
  • PreparedStatementHandler:对应 JDBC PreparedStatement(预编译 SQL 语句)
  • CallableStatementHandler:对应 JDBC CallableStatement(存储过程)

方法解读

  • Statement prepare(Connection connection, Integer transactionTimeout):通过 Connection 实例获取 Statement 对象
  • void parameterize(Statement statement):为 Statement 对象设置参数,特别是 PreparedStatement 设置 ? 占位符对应的实参
  • void batch(Statement statement):添加批量数据
  • int update(Statement statement):通过 Statement 实例来执行修改(增、删、改)操作
  • <E> List<E> query(Statement statement, ResultHandler resultHandler):查询并处理结果集
  • <E> List<E> queryCursor(Statement statement):游标
  • BoundSql getBoundSql():获取底层关联的 SQL与参数元信息对象
  • ParameterHandler getParameterHandler():获取底层关联的参数处理器

RountingStatementHandler

MyBatis 框架内部使用此类来处理 Statement, 内部通过创建 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler 对象进行正真的处理

	// 通过构造 RoutingStatementHandler 时候根据 StatementType 来创建对应的 StatementHandler
	// 在 Mapper.xml 中 insert/update/delete/select 标签中有 statementType 属性进行设置,默认是 PREPARED
	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());
	  }
	
	}

BaseStatementHandler

StatementHandler 通用实现,如 getBoundSql、getParameterHandler 以及 prepare 方法,通过定义 instantiateStatement(Connection connection) 模板方法来获取不同类型的 Statment 实例。

public abstract class BaseStatementHandler implements StatementHandler {
  // MyBatis 全局配置对象
  protected final Configuration configuration;
  // 对象工厂
  protected final ObjectFactory objectFactory;
  // 类型处理器注册表
  protected final TypeHandlerRegistry typeHandlerRegistry;
  // 结果集处理器
  protected final ResultSetHandler resultSetHandler;
  // 参数处理器
  protected final ParameterHandler parameterHandler;
  // 执行器 
  protected final Executor executor;
  // XML insert/update/select/delete 标签所有元信息对象
  protected final MappedStatement mappedStatement;
  // 分页设置
  protected final RowBounds rowBounds;
  // SQL 语句和参数映射等元信息
  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) { 
      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);
  }
}

SimpleStatementHandler

简单语句处理,对应 JDBC Statement(静态 SQL),所有涉及数据库操作则通过调用 Statement API

public class SimpleStatementHandler extends BaseStatementHandler {

  public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    // 通过 BoundSql 获取 SQL 语句,然后调用 JDBC Statement API 执行操作
    // 根据主键生成策略不同回填主键值
    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;
  }

  @Override
  public void batch(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    statement.addBatch(sql);
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  	// 通过 BoundSql 获取 SQL 语句,然后调用 JDBC Statement API 执行操作并对结果集进行处理
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.handleResultSets(statement);
  }

  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
  // 通过 BoundSql 获取 SQL 语句,然后调用 JDBC Statement API 执行操作并对结果集进行处理
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.handleCursorResultSets(statement);
  }

  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
  	// 通过原生 JDBC Connectio API 创建 Statement 对象设置相应的结果集类型
  	// select 标签可以设置 resultSetType 属性为 FORWARD_ONLY、SCROLL_INSENSITIVE 、SCROLL_SENSITIVE
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.createStatement();
    } else {
      // 设置 select 标签里面的 resultSetType,并把结果集的并发模型设置为只读
      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }
 
  
  @Override
  public void parameterize(Statement statement) {
    // N/A
    // 静态 SQL 不存在设置参数空实现
  }

}

PreparedStatementHandler

public class PreparedStatementHandler extends BaseStatementHandler {

  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public int update(Statement statement) throws SQLException {
  	// 调用 JDBC PreparedStatement  API 执行操作
    // 根据主键生成策略不同回填主键值
    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 void batch(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.addBatch();
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  	// 调用 JDBC PreparedStatement  API 执行操作
  	// 使用结果处理器对结果集进行处理
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
  	// 调用 JDBC PreparedStatement  API 执行操作
  	// 使用结果处理器对结果集进行处理
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleCursorResultSets(ps);
  }

  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    // 通过 BoundSql 获取 SQL 语句,通过 MappedStatement 对象获取主键生成策略以及主键列,
    // 然后调用 JDBC Connection API 获得 PreparedStatement 实例
    String sql = boundSql.getSql();
    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);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

  @Override
  public void parameterize(Statement statement) throws SQLException {
  	// 通过参数处理器对预编译中的 ? 占位符进行值设置
    parameterHandler.setParameters((PreparedStatement) statement);
  }

}

CallableStatementHandler

与上面的 PreparedStatementHandler 源码差不多,只是要设置存储过程输出参数。通过 JDBC 原生 Connection API 获取 CallableStatement 实例,然后通过该实例操作数据库。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MyBatis执行流程可以分为以下几个步骤: 1. 解析配置文件:MyBatis首先会解析配置文件,包括数据库连接信息、映射文件等内容。配置文件中的信息将被用于后续的操作。 2. 创建SqlSessionFactory:根据配置文件中的信息,MyBatis会创建一个SqlSessionFactory对象。SqlSessionFactory是MyBatis的核心对象,它负责创建SqlSession对象。 3. 创建SqlSession:通过SqlSessionFactory的openSession()方法MyBatis会创建一个SqlSession对象。SqlSession提供了执行SQL语句方法,可以与数据库进行交互。 4. 解析Mapper文件:在执行SQL语句之前,MyBatis会解析Mapper文件。Mapper文件中定义了与数据库交互的SQL语句,包括增删改查等操作。 5. 执行SQL语句:根据Mapper文件中定义的SQL语句MyBatis会使用StatementHandler对象执行SQL语句StatementHandler负责处理SQL语句的参数设置、预编译等操作。 6. 处理结果集:执行SQL语句后,MyBatis会使用ResultSetHandler对象处理查询结果集。ResultSetHandler负责将结果集转化为Java对象。 7. 返回结果:最后,MyBatis会将结果返回给调用方。 总的来说,MyBatis执行流程包括配置文件解析、创建SqlSessionFactory、创建SqlSession、解析Mapper文件、执行SQL语句、处理结果集和返回结果等步骤。通过这个流程,MyBatis能够方便地与数据库进行交互。 [3 [1<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [mybatis执行流程](https://blog.csdn.net/u012391423/article/details/118559651)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [MyBatis执行流程,写得太好了!](https://blog.csdn.net/MarkerHub/article/details/113577930)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿大叔文海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值