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

      

  • 相关阅读:
    2016"百度之星"
    codeforces 55 div2 C.Title 模拟
    codeforces 98 div2 C.History 水题
    codeforces 97 div2 C.Replacement 水题
    codeforces 200 div2 C. Rational Resistance 思路题
    bzoj 2226 LCMSum 欧拉函数
    hdu 1163 九余数定理
    51nod 1225 余数的和 数学
    bzoj 2818 gcd 线性欧拉函数
    Codeforces Round #332 (Div. 2)D. Spongebob and Squares 数学
  • 原文地址:https://www.cnblogs.com/juniorMa/p/13930715.html
Copyright © 2011-2022 走看看