BatchExecutor.java

  1. /*
  2.  *    Copyright 2009-2021 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.executor;

  17. import java.sql.BatchUpdateException;
  18. import java.sql.Connection;
  19. import java.sql.SQLException;
  20. import java.sql.Statement;
  21. import java.util.ArrayList;
  22. import java.util.Collections;
  23. import java.util.List;

  24. import org.apache.ibatis.cursor.Cursor;
  25. import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
  26. import org.apache.ibatis.executor.keygen.KeyGenerator;
  27. import org.apache.ibatis.executor.keygen.NoKeyGenerator;
  28. import org.apache.ibatis.executor.statement.StatementHandler;
  29. import org.apache.ibatis.mapping.BoundSql;
  30. import org.apache.ibatis.mapping.MappedStatement;
  31. import org.apache.ibatis.session.Configuration;
  32. import org.apache.ibatis.session.ResultHandler;
  33. import org.apache.ibatis.session.RowBounds;
  34. import org.apache.ibatis.transaction.Transaction;

  35. /**
  36.  * @author Jeff Butler
  37.  */
  38. public class BatchExecutor extends BaseExecutor {

  39.   public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;

  40.   private final List<Statement> statementList = new ArrayList<>();
  41.   private final List<BatchResult> batchResultList = new ArrayList<>();
  42.   private String currentSql;
  43.   private MappedStatement currentStatement;

  44.   public BatchExecutor(Configuration configuration, Transaction transaction) {
  45.     super(configuration, transaction);
  46.   }

  47.   @Override
  48.   public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
  49.     final Configuration configuration = ms.getConfiguration();
  50.     final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
  51.     final BoundSql boundSql = handler.getBoundSql();
  52.     final String sql = boundSql.getSql();
  53.     final Statement stmt;
  54.     if (sql.equals(currentSql) && ms.equals(currentStatement)) {
  55.       int last = statementList.size() - 1;
  56.       stmt = statementList.get(last);
  57.       applyTransactionTimeout(stmt);
  58.       handler.parameterize(stmt);// fix Issues 322
  59.       BatchResult batchResult = batchResultList.get(last);
  60.       batchResult.addParameterObject(parameterObject);
  61.     } else {
  62.       Connection connection = getConnection(ms.getStatementLog());
  63.       stmt = handler.prepare(connection, transaction.getTimeout());
  64.       handler.parameterize(stmt);    // fix Issues 322
  65.       currentSql = sql;
  66.       currentStatement = ms;
  67.       statementList.add(stmt);
  68.       batchResultList.add(new BatchResult(ms, sql, parameterObject));
  69.     }
  70.     handler.batch(stmt);
  71.     return BATCH_UPDATE_RETURN_VALUE;
  72.   }

  73.   @Override
  74.   public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
  75.       throws SQLException {
  76.     Statement stmt = null;
  77.     try {
  78.       flushStatements();
  79.       Configuration configuration = ms.getConfiguration();
  80.       StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
  81.       Connection connection = getConnection(ms.getStatementLog());
  82.       stmt = handler.prepare(connection, transaction.getTimeout());
  83.       handler.parameterize(stmt);
  84.       return handler.query(stmt, resultHandler);
  85.     } finally {
  86.       closeStatement(stmt);
  87.     }
  88.   }

  89.   @Override
  90.   protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
  91.     flushStatements();
  92.     Configuration configuration = ms.getConfiguration();
  93.     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
  94.     Connection connection = getConnection(ms.getStatementLog());
  95.     Statement stmt = handler.prepare(connection, transaction.getTimeout());
  96.     handler.parameterize(stmt);
  97.     Cursor<E> cursor = handler.queryCursor(stmt);
  98.     stmt.closeOnCompletion();
  99.     return cursor;
  100.   }

  101.   @Override
  102.   public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
  103.     try {
  104.       List<BatchResult> results = new ArrayList<>();
  105.       if (isRollback) {
  106.         return Collections.emptyList();
  107.       }
  108.       for (int i = 0, n = statementList.size(); i < n; i++) {
  109.         Statement stmt = statementList.get(i);
  110.         applyTransactionTimeout(stmt);
  111.         BatchResult batchResult = batchResultList.get(i);
  112.         try {
  113.           batchResult.setUpdateCounts(stmt.executeBatch());
  114.           MappedStatement ms = batchResult.getMappedStatement();
  115.           List<Object> parameterObjects = batchResult.getParameterObjects();
  116.           KeyGenerator keyGenerator = ms.getKeyGenerator();
  117.           if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
  118.             Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
  119.             jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
  120.           } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
  121.             for (Object parameter : parameterObjects) {
  122.               keyGenerator.processAfter(this, ms, stmt, parameter);
  123.             }
  124.           }
  125.           // Close statement to close cursor #1109
  126.           closeStatement(stmt);
  127.         } catch (BatchUpdateException e) {
  128.           StringBuilder message = new StringBuilder();
  129.           message.append(batchResult.getMappedStatement().getId())
  130.               .append(" (batch index #")
  131.               .append(i + 1)
  132.               .append(")")
  133.               .append(" failed.");
  134.           if (i > 0) {
  135.             message.append(" ")
  136.                 .append(i)
  137.                 .append(" prior sub executor(s) completed successfully, but will be rolled back.");
  138.           }
  139.           throw new BatchExecutorException(message.toString(), e, results, batchResult);
  140.         }
  141.         results.add(batchResult);
  142.       }
  143.       return results;
  144.     } finally {
  145.       for (Statement stmt : statementList) {
  146.         closeStatement(stmt);
  147.       }
  148.       currentSql = null;
  149.       statementList.clear();
  150.       batchResultList.clear();
  151.     }
  152.   }

  153. }