zoukankan      html  css  js  c++  java
  • 与Spring整合的Mybatis没法真正使用BatchExecutor

      标题可能有点懵人,我要表达的意思就是在和Spring整合后,Mybatis的确能够使用到BatchExecutor,但是看现象不是批量执行的,而是单条执行。

      先上代码

    @Test
      public void testAddUser() throws InterruptedException{
          for (int i = 0;i<20;i++) {
              User user = new User();
              user.setUserId(String.valueOf(i));
    //          user.setUserName("xdp_gacl_白虎神皇");
              user.setUserBirthday(new Date().toString());
              user.setUserSalary((double) (10 + i));
              userService.addUser(user);
          }
          Thread.sleep(5000);
      }
    @Override
        @Transactional(propagation = Propagation.REQUIRED)
        public void addUser(User user) {
            
    //        try {
    //        ((UserServiceI) AopContext.currentProxy()).addUserScore();
    //        }catch(Exception e) {
    ////            
    //        }
            userMapper.insert(user);
    //        int t = 1/0;
            
        }

      有趣的现象就是 我发现每次都会走到 ,注意是每次,说明每次执行  userService.addUser(user); 都会重新搞一次SqlSession,我猜测是userMapper的insert 都会重新获取SqlSession执行

     private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

       那么怎么才能使用batch方式呢,我这里只找到直接通过mybatis方式,没法通过spring的方式

    @Test
      public void saveDeptBatchOne() throws Exception {
          SqlSessionFactory sessionFactory = SpringContextUtils.getBean("sqlSessionFactory", SqlSessionFactory.class);
          SqlSession session = sessionFactory.openSession();
          try {
              UserMapper deptMapper = (UserMapper) session.getMapper(UserMapper.class);
              long start =System.currentTimeMillis();
              for (int i = 0; i <100 ; i++) {
                  User user = new User();
                  user.setUserId(String.valueOf(i));
                  user.setUserBirthday(new Date().toString());
                  user.setUserSalary((double) (10 + i));
                  deptMapper.insert(user);
              }
              long end =System.currentTimeMillis();
              System.out.println("BATCH耗时:"+(end-start));
              //非BATCH批量耗时 耗时:938
          } catch (Exception e) {
              e.printStackTrace();
          } finally {
              session.commit();
              session.close();
          }
      }

    经过测试 通过batch方式的耗时是550ms-620ms

         而通过spring方式耗时 1200ms

      顺带提一下BATCH方式的原理和一个不大不小的问题

      原理

      都知道mybatis是对jdbc的封装,mybatis中的statement默认的类型是 PreparedStatement

          Class.forName("com.mysql.jdbc.Driver");
             Connection conn = DriverManager.getConnection(dbUrl, user, password);
             PreparedStatement pstmt = conn.prepareStatement("update content set introtext=? where id=?");
    
             for(int i=0; i<10000; i++){
                 pstmt.setString(1, "abc"+i);
                 pstmt.setInt(2, id);
                 pstmt.addBatch();//添加到同一个批处理中
             }
    
             pstmt.executeBatch();//执行批处理

      上面就是jdbc使用batch的方式,再看看BatchExecutor是怎么做的

     @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);
          handler.parameterize(stmt);//fix Issues 322
          BatchResult batchResult = batchResultList.get(last);
          batchResult.addParameterObject(parameterObject);
        } else {
          Connection connection = getConnection(ms.getStatementLog());
          stmt = handler.prepare(connection);
          handler.parameterize(stmt);    //fix Issues 322
          currentSql = sql;
          currentStatement = ms;
          statementList.add(stmt);
          batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }
      // handler.parameterize(stmt);
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
      }
     @Override
      public void batch(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.addBatch();
      }

      batch什么时候执行呢?在SqlSession的commit里会调用Executor的 doFlushStatements

    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
            Statement stmt = statementList.get(i);
            BatchResult batchResult = batchResultList.get(i);
            try {
              batchResult.setUpdateCounts(stmt.executeBatch());

      如果要使用batch带来的好处,有一点必须注意,获取数据源jdbc连接必须带上 rewriteBatchedStatements=true

    jdbc:mysql://10.3x.1x6.1x1:3306/rxxxx1?rewriteBatchedStatements=true

      batch方式有什么问题呢?有

      1 如果是自增主键,do类带不回来

      2 并不会自动提交,必须  session.commit();

    下一篇就是分析源码了,为啥执行mapper的方法每次都创建sqlsession

      

  • 相关阅读:
    SSL评测
    EF+SQLSERVER控制并发下抢红包减余额(改进)
    关于游标嵌套时@@FETCH_STATUS的值
    windows下限制Redis端口只能由本机访问
    windows下配置Redis
    Node.js 使用gm处理图像
    Git 与其他系统
    git-svn 简易 操作指南
    git-svn — 让git和svn协同工作
    Git和SVN共存的方法
  • 原文地址:https://www.cnblogs.com/juniorMa/p/13930715.html
Copyright © 2011-2022 走看看