View Javadoc
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  
18  import java.sql.BatchUpdateException;
19  import java.sql.Connection;
20  import java.sql.SQLException;
21  import java.sql.Statement;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  
26  import org.apache.ibatis.cursor.Cursor;
27  import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
28  import org.apache.ibatis.executor.keygen.KeyGenerator;
29  import org.apache.ibatis.executor.keygen.NoKeyGenerator;
30  import org.apache.ibatis.executor.statement.StatementHandler;
31  import org.apache.ibatis.mapping.BoundSql;
32  import org.apache.ibatis.mapping.MappedStatement;
33  import org.apache.ibatis.session.Configuration;
34  import org.apache.ibatis.session.ResultHandler;
35  import org.apache.ibatis.session.RowBounds;
36  import org.apache.ibatis.transaction.Transaction;
37  
38  /**
39   * @author Jeff Butler
40   */
41  public class BatchExecutor extends BaseExecutor {
42  
43    public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;
44  
45    private final List<Statement> statementList = new ArrayList<>();
46    private final List<BatchResult> batchResultList = new ArrayList<>();
47    private String currentSql;
48    private MappedStatement currentStatement;
49  
50    public BatchExecutor(Configuration configuration, Transaction transaction) {
51      super(configuration, transaction);
52    }
53  
54    @Override
55    public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
56      final Configuration configuration = ms.getConfiguration();
57      final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT,
58          null, null);
59      final BoundSql boundSql = handler.getBoundSql();
60      final String sql = boundSql.getSql();
61      final Statement stmt;
62      if (sql.equals(currentSql) && ms.equals(currentStatement)) {
63        int last = statementList.size() - 1;
64        stmt = statementList.get(last);
65        applyTransactionTimeout(stmt);
66        handler.parameterize(stmt);// fix Issues 322
67        BatchResult batchResult = batchResultList.get(last);
68        batchResult.addParameterObject(parameterObject);
69      } else {
70        Connection connection = getConnection(ms.getStatementLog());
71        stmt = handler.prepare(connection, transaction.getTimeout());
72        handler.parameterize(stmt); // fix Issues 322
73        currentSql = sql;
74        currentStatement = ms;
75        statementList.add(stmt);
76        batchResultList.add(new BatchResult(ms, sql, parameterObject));
77      }
78      handler.batch(stmt);
79      return BATCH_UPDATE_RETURN_VALUE;
80    }
81  
82    @Override
83    public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
84        ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
85      Statement stmt = null;
86      try {
87        flushStatements();
88        Configuration configuration = ms.getConfiguration();
89        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds,
90            resultHandler, boundSql);
91        Connection connection = getConnection(ms.getStatementLog());
92        stmt = handler.prepare(connection, transaction.getTimeout());
93        handler.parameterize(stmt);
94        return handler.query(stmt, resultHandler);
95      } finally {
96        closeStatement(stmt);
97      }
98    }
99  
100   @Override
101   protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
102       throws SQLException {
103     flushStatements();
104     Configuration configuration = ms.getConfiguration();
105     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
106     Connection connection = getConnection(ms.getStatementLog());
107     Statement stmt = handler.prepare(connection, transaction.getTimeout());
108     handler.parameterize(stmt);
109     Cursor<E> cursor = handler.queryCursor(stmt);
110     stmt.closeOnCompletion();
111     return cursor;
112   }
113 
114   @Override
115   public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
116     try {
117       List<BatchResult> results = new ArrayList<>();
118       if (isRollback) {
119         return Collections.emptyList();
120       }
121       for (int i = 0, n = statementList.size(); i < n; i++) {
122         Statement stmt = statementList.get(i);
123         applyTransactionTimeout(stmt);
124         BatchResult batchResult = batchResultList.get(i);
125         try {
126           batchResult.setUpdateCounts(stmt.executeBatch());
127           MappedStatement ms = batchResult.getMappedStatement();
128           List<Object> parameterObjects = batchResult.getParameterObjects();
129           KeyGenerator keyGenerator = ms.getKeyGenerator();
130           if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
131             Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
132             jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
133           } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { // issue #141
134             for (Object parameter : parameterObjects) {
135               keyGenerator.processAfter(this, ms, stmt, parameter);
136             }
137           }
138           // Close statement to close cursor #1109
139           closeStatement(stmt);
140         } catch (BatchUpdateException e) {
141           StringBuilder message = new StringBuilder();
142           message.append(batchResult.getMappedStatement().getId()).append(" (batch index #").append(i + 1).append(")")
143               .append(" failed.");
144           if (i > 0) {
145             message.append(" ").append(i)
146                 .append(" prior sub executor(s) completed successfully, but will be rolled back.");
147           }
148           throw new BatchExecutorException(message.toString(), e, results, batchResult);
149         }
150         results.add(batchResult);
151       }
152       return results;
153     } finally {
154       for (Statement stmt : statementList) {
155         closeStatement(stmt);
156       }
157       currentSql = null;
158       statementList.clear();
159       batchResultList.clear();
160     }
161   }
162 
163 }