首页 > 其他分享 >mybatis中BatchExecutor的使用

mybatis中BatchExecutor的使用

时间:2024-08-11 18:27:56浏览次数:14  
标签:batchResult BatchExecutor 使用 stmt ms sql mybatis 执行

这部分内容需要掌握mysql的sql执行流程和事务控制才能理解,可以参考下我的相关文章。

mybatis sql执行流程

mybatis事务控制

一、BatchExecutor的介绍

BatchExecutor 是mybatis提供的一个执行器, 用于执行批量更新操作,性能上比使用foreach标签拼sql要高,使用方式上也更方便。BatchExecutor 只是对更新操作做了增强,查询操作和普通的执行器是一样的。

二、BatchExecutor的使用

使用BatchExecutor时dao方法对应的sql是每次更新一条数据的,在java中循环数据集合攒sql,最后在使用jdbc的batch操作把sql发送到数据库执行

注意下边update方法批量执行sql的前提是要开启事务,原因需要在分析BatchExecutor的源码时解答

@Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Transactional
    public void update() {
        SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
        Account account1 = new Account();
        account1.setId("zs");
        account1.setMoney(100);
        Account account2 = new Account();
        account2.setId("ls");
        account1.setMoney(200);
        List<Account> accountList = new ArrayList<>();
        accountList.add(account1);
        accountList.add(account2);

        //通过sqlsession获取mapper接口的代理对象
        IAccountDao accountMapper = sqlSessionTemplate.getMapper(IAccountDao.class);
        for (Account account : accountList) {
            //这个update方法每执行一次就会攒一个sql
            //注意只有当前service方法开启事务的情况下才会有这种攒sql的效果,
            //如果没有事务,这个update方法每次执行都是一个新的事务,新的数据库连接,sql执行完就会提交事务,不会攒sql,还是在单条执行
            accountMapper.update(account);
        }
        //这句就是把攒的sql发到数据库执行
        sqlSessionTemplate.flushStatements();

    }

三、BatchExecutor源码分析

BatchExecutor的关键在doUpdate方法

public class BatchExecutor extends BaseExecutor {
    @Override
  public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
    final Configuration configuration = ms.getConfiguration();
    final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
    final BoundSql boundSql = handler.getBoundSql();
    final String sql = boundSql.getSql();
    final Statement stmt;
    if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      int last = statementList.size() - 1;
      stmt = statementList.get(last);
      applyTransactionTimeout(stmt);
     handler.parameterize(stmt);//fix Issues 322
      BatchResult batchResult = batchResultList.get(last);
      batchResult.addParameterObject(parameterObject);
    } else {
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);    //fix Issues 322
      currentSql = sql;
      currentStatement = ms;
      statementList.add(stmt);
      batchResultList.add(new BatchResult(ms, sql, parameterObject));
    }
    // 上边是对参数的处理等准备工作,下边handler的batch方法是关键
    handler.batch(stmt);
    return BATCH_UPDATE_RETURN_VALUE;
  }
    
  //调用sqlsession.flushStatements方法时最终会调用到这个方法,把攒的sql刷到数据库
  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      List<BatchResult> results = new ArrayList<BatchResult>();
      if (isRollback) {
        return Collections.emptyList();
      }
      for (int i = 0, n = statementList.size(); i < n; i++) {
        Statement stmt = statementList.get(i);
        applyTransactionTimeout(stmt);
        BatchResult batchResult = batchResultList.get(i);
        try {
          //这个stmt.executeBatch()方法就是把sql批量刷到数据库,这是jdbc提供的方法
          batchResult.setUpdateCounts(stmt.executeBatch());
          MappedStatement ms = batchResult.getMappedStatement();
          List<Object> parameterObjects = batchResult.getParameterObjects();
          KeyGenerator keyGenerator = ms.getKeyGenerator();
          if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
            Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
            jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
          } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
            for (Object parameter : parameterObjects) {
              keyGenerator.processAfter(this, ms, stmt, parameter);
            }
          }
        } catch (BatchUpdateException e) {
          StringBuilder message = new StringBuilder();
          message.append(batchResult.getMappedStatement().getId())
              .append(" (batch index #")
              .append(i + 1)
              .append(")")
              .append(" failed.");
          if (i > 0) {
            message.append(" ")
                .append(i)
                .append(" prior sub executor(s) completed successfully, but will be rolled back.");
          }
          throw new BatchExecutorException(message.toString(), e, results, batchResult);
        }
        results.add(batchResult);
      }
      return results;
    } finally {
      for (Statement stmt : statementList) {
        closeStatement(stmt);
      }
      currentSql = null;
      statementList.clear();
      batchResultList.clear();
    }
  }

}

在来分析下handler的batch方法,我们以PreparedStatementHandler 为例

//部分源码
public class PreparedStatementHandler extends BaseStatementHandler {
  @Override
  public void batch(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //这里就是调用了jdbc的addBatch方法来攒sql
    ps.addBatch();
  }
}

上边的源码总结下来就是使用 statement.addBatch()攒sql,使用stmt.executeBatch() 执行攒的sql,

至于为什么必须要求service方法开启事务呢,因为jdbc批量执行sql的原理就是要把多个sql攒到同一个数据库连接上然后一次执行,当不开启真个方法事务时,mapper方法每一次执行完都会提交事务,提交事务前就会把攒的sql刷到数据库,然后释放连接,下次mapper方法执行时又会重新获取数据库连接,重新开启事务。所以每一次都只是执行一个sql,没有批量的效果。

这部分内容需要掌握mysql的sql执行流程和事务控制才能理解,可以参考下我的相关文章。

mybatis sql执行流程

mybatis事务控制

标签:batchResult,BatchExecutor,使用,stmt,ms,sql,mybatis,执行
From: https://www.cnblogs.com/chengxuxiaoyuan/p/18353718

相关文章

  • 如何使用Memcached缓存图片信息
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满。学成后可......
  • 交互题使用指南
    一般oi场上的交互题都是使用Grader交互(cf是stdio交互)。本文讲解一下怎么做交互题,评测交互题。怎么做基本知识题目会给你几个函数接口,一般作为询问的方式。出题人会写一个grader.cpp里面就包含了这几个函数。调用grader里面的函数的方法是:加上题目给的头文件/在前面......
  • mac使用
     常用快捷键打开聚合:command+空格查看隐藏文件:command+shift+.最小化窗口:command+m隐藏窗口:command+H关闭程序:command+q```javaMac快捷方式复制文件路径:opt+command+c打开全文搜索:opt+command+空格 访达中command+shift+g打开文件目录 撤销:command+z......
  • xrender中的FormRender使用示例
    xrender是阿里的中后台「表单/表格/图表」开箱即用解决方案。先采用在线工具创建一个简单的schema:simple.tsexportdefault{"type":"object","properties":{"title":{"title":"标题","type&qu......
  • 解决zabbix用户无法使用psql命令的问题
    解决zabbix用户无法使用psql命令的问题1.创建文件夹登录zabbix用户,创建所需的文件夹。su-zabbixcd/home/zabbixmkdirlibmkdirzabbix_envmkdirpg_binvitest.sh#创建测试脚本vitest.shcattest.shPGSHELL_CONFDIR="$1"source$PGSHELL_CONFDIR/zabbix_shell......
  • 使用grpc开发RPC服务
    grpc简介gRPC是谷歌开源的一款跨平台、高性能的RPC框架,笔者目前主要使用它来进行后端微服务的开发。可能会有的同学对RPC不太熟悉,其实在笔者看来,RPC和HTTP并无多大的区别都是一种调用方式,区别则是在于RPC会限制传输协议、传输的参数等,以此换取高效的传输流程,比如grpc就使用的是g......
  • zabbix使用自动发现对pg的复制槽进行状态监控
    zabbix使用自动发现对pgsql的复制槽进行状态监控环境介绍:本例中安装zabbix开源软件和postgres,软件版本信息如下:软件版本zabbixZabbix6.4.0zabbix-server上postgres14.7zabbix-agent6.4.17zabbix-agent上postgres16.3主机信息如下:RedHatEnterprise......
  • 解决yum命令无法使用的情况
    1.CentOSyum出现无法使用的情况1问题产生​ 新安装好的虚拟机无法使用yum命令来安装wget,一直提示报错。已加载插件:fastestmirrorCouldnotretrievemirrorlisthttp://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stockerrorwas14:curl#7-"Failedt......
  • linux 使用iio 通过I2C 方式采集实例
    IIO(IndustrialI/O)子系统通过I2C方式采集数据的实例。这个例子包括驱动程序和用户空间应用程序。首先,让我们创建一个简单的IIO驱动程序,它通过I2C接口与ADC(模数转换器)通信,并通过PCI总线连接到系统。1.驱动程序(my_iio_driver.c):```c#include<linux/module.h>......
  • 使用条件语句登录QQ(Pythonn实验实训项目)
    实验目的1、掌握变量的创建方法及变量赋值运算的使用方法;2、掌握的掌握if-elif--else条件语句和循环语句的嵌套使用;3、掌握比较运算符的应用;4、掌握输入语句的使用方法;5、掌握continue和break语句的用法。二、实验内容使用条件语句实现QQ登录:若用户名和密码都输入正确,则......