BatchExecutor.java

  1. /*
  2.  *    Copyright 2009-2023 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.  *       https://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,
  51.         null, null);
  52.     final BoundSql boundSql = handler.getBoundSql();
  53.     final String sql = boundSql.getSql();
  54.     final Statement stmt;
  55.     if (sql.equals(currentSql) && ms.equals(currentStatement)) {
  56.       int last = statementList.size() - 1;
  57.       stmt = statementList.get(last);
  58.       applyTransactionTimeout(stmt);
  59.       handler.parameterize(stmt);// fix Issues 322
  60.       BatchResult batchResult = batchResultList.get(last);
  61.       batchResult.addParameterObject(parameterObject);
  62.     } else {
  63.       Connection connection = getConnection(ms.getStatementLog());
  64.       stmt = handler.prepare(connection, transaction.getTimeout());
  65.       handler.parameterize(stmt); // fix Issues 322
  66.       currentSql = sql;
  67.       currentStatement = ms;
  68.       statementList.add(stmt);
  69.       batchResultList.add(new BatchResult(ms, sql, parameterObject));
  70.     }
  71.     handler.batch(stmt);
  72.     return BATCH_UPDATE_RETURN_VALUE;
  73.   }

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

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

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

  152. }