1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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);
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);
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())) {
134 for (Object parameter : parameterObjects) {
135 keyGenerator.processAfter(this, ms, stmt, parameter);
136 }
137 }
138
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 }