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