ShardingSpherePreparedStatement.java 20.0 KB
Newer Older
T
terrymanu 已提交
1
/*
2 3 4 5 6 7
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
T
terrymanu 已提交
8
 *
T
terrymanu 已提交
9
 *     http://www.apache.org/licenses/LICENSE-2.0
T
terrymanu 已提交
10
 *
T
terrymanu 已提交
11 12 13 14 15 16 17
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

18
package org.apache.shardingsphere.driver.jdbc.core.statement;
T
terrymanu 已提交
19

20
import com.google.common.base.Strings;
21
import lombok.Getter;
22 23 24 25 26 27 28 29 30
import org.apache.shardingsphere.driver.executor.PreparedStatementExecutor;
import org.apache.shardingsphere.driver.executor.batch.BatchExecutionUnit;
import org.apache.shardingsphere.driver.executor.batch.BatchPreparedStatementExecutor;
import org.apache.shardingsphere.driver.jdbc.adapter.AbstractPreparedStatementAdapter;
import org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
import org.apache.shardingsphere.driver.jdbc.core.constant.SQLExceptionConstant;
import org.apache.shardingsphere.driver.jdbc.core.resultset.GeneratedKeysResultSet;
import org.apache.shardingsphere.driver.jdbc.core.resultset.ShardingSphereResultSet;
import org.apache.shardingsphere.driver.jdbc.core.statement.metadata.ShardingSphereParameterMetaData;
31 32 33 34 35 36
import org.apache.shardingsphere.infra.binder.LogicSQL;
import org.apache.shardingsphere.infra.binder.SQLStatementContextFactory;
import org.apache.shardingsphere.infra.binder.segment.insert.keygen.GeneratedKeyContext;
import org.apache.shardingsphere.infra.binder.statement.SQLStatementContext;
import org.apache.shardingsphere.infra.binder.statement.dml.InsertStatementContext;
import org.apache.shardingsphere.infra.binder.statement.dml.SelectStatementContext;
37
import org.apache.shardingsphere.infra.config.properties.ConfigurationPropertyKey;
38
import org.apache.shardingsphere.infra.context.kernel.KernelProcessor;
39
import org.apache.shardingsphere.infra.context.schema.SchemaContexts;
40
import org.apache.shardingsphere.infra.database.type.DatabaseTypeRegistry;
41 42
import org.apache.shardingsphere.infra.exception.ShardingSphereException;
import org.apache.shardingsphere.infra.executor.kernel.InputGroup;
L
Liang Zhang 已提交
43
import org.apache.shardingsphere.infra.executor.sql.ExecutorConstant;
44 45
import org.apache.shardingsphere.infra.executor.sql.QueryResult;
import org.apache.shardingsphere.infra.executor.sql.context.ExecutionContext;
L
Liang Zhang 已提交
46 47 48
import org.apache.shardingsphere.infra.executor.sql.log.SQLLogger;
import org.apache.shardingsphere.infra.executor.sql.raw.RawSQLExecuteUnit;
import org.apache.shardingsphere.infra.executor.sql.raw.execute.RawJDBCExecutor;
L
Liang Zhang 已提交
49
import org.apache.shardingsphere.infra.executor.sql.raw.execute.callback.RawSQLExecutorCallback;
L
Liang Zhang 已提交
50
import org.apache.shardingsphere.infra.executor.sql.raw.group.RawExecuteGroupEngine;
51 52 53 54 55
import org.apache.shardingsphere.infra.executor.sql.resourced.jdbc.StatementExecuteUnit;
import org.apache.shardingsphere.infra.executor.sql.resourced.jdbc.executor.SQLExecutor;
import org.apache.shardingsphere.infra.executor.sql.resourced.jdbc.group.PreparedStatementExecuteGroupEngine;
import org.apache.shardingsphere.infra.executor.sql.resourced.jdbc.group.StatementOption;
import org.apache.shardingsphere.infra.executor.sql.resourced.jdbc.queryresult.StreamQueryResult;
56 57
import org.apache.shardingsphere.infra.merge.MergeEngine;
import org.apache.shardingsphere.infra.merge.result.MergedResult;
58
import org.apache.shardingsphere.infra.metadata.schema.ShardingSphereSchema;
59
import org.apache.shardingsphere.infra.rule.type.DataNodeContainedRule;
60
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
61
import org.apache.shardingsphere.infra.parser.ShardingSphereSQLParserEngine;
T
tristaZero 已提交
62 63
import org.apache.shardingsphere.sql.parser.sql.common.statement.SQLStatement;
import org.apache.shardingsphere.sql.parser.sql.common.statement.dal.DALStatement;
T
terrymanu 已提交
64

65
import java.sql.ParameterMetaData;
T
terrymanu 已提交
66 67 68
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
69
import java.sql.Statement;
T
terrymanu 已提交
70
import java.util.ArrayList;
T
terrymanu 已提交
71
import java.util.Collection;
72
import java.util.LinkedList;
T
terrymanu 已提交
73
import java.util.List;
L
Liang Zhang 已提交
74
import java.util.Optional;
75
import java.util.stream.Collectors;
T
terrymanu 已提交
76

T
terrymanu 已提交
77
/**
78
 * ShardingSphere prepared statement.
T
terrymanu 已提交
79
 */
80
public final class ShardingSpherePreparedStatement extends AbstractPreparedStatementAdapter {
T
terrymanu 已提交
81
    
82
    @Getter
83
    private final ShardingSphereConnection connection;
84
    
J
Juan Pan(Trista) 已提交
85 86
    private final SchemaContexts schemaContexts;
    
T
terrymanu 已提交
87 88
    private final String sql;
    
89 90 91 92
    private final List<PreparedStatement> statements;
    
    private final List<List<Object>> parameterSets;
    
93 94
    private final SQLStatement sqlStatement;
    
95 96
    private final StatementOption statementOption;
    
97 98 99
    @Getter
    private final ParameterMetaData parameterMetaData;
    
100
    private final PreparedStatementExecutor preparedStatementExecutor;
T
tristaZero 已提交
101
    
L
Liang Zhang 已提交
102
    private final RawJDBCExecutor rawExecutor;
L
Liang Zhang 已提交
103
    
T
tristaZero 已提交
104 105
    private final BatchPreparedStatementExecutor batchPreparedStatementExecutor;
    
106 107
    private final Collection<Comparable<?>> generatedValues = new LinkedList<>();
    
108 109
    private final KernelProcessor kernelProcessor;
    
L
Liang Zhang 已提交
110
    private ExecutionContext executionContext;
111 112 113
    
    private ResultSet currentResultSet;
    
114
    public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql) throws SQLException {
115 116 117
        this(connection, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, false);
    }
    
118
    public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
119
        this(connection, sql, resultSetType, resultSetConcurrency, ResultSet.HOLD_CURSORS_OVER_COMMIT, false);
T
terrymanu 已提交
120 121
    }
    
122
    public ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql, final int autoGeneratedKeys) throws SQLException {
T
tristaZero 已提交
123 124 125
        this(connection, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT, Statement.RETURN_GENERATED_KEYS == autoGeneratedKeys);
    }
    
126 127
    public ShardingSpherePreparedStatement(
            final ShardingSphereConnection connection, final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException {
T
tristaZero 已提交
128
        this(connection, sql, resultSetType, resultSetConcurrency, resultSetHoldability, false);
T
terrymanu 已提交
129 130
    }
    
131 132
    private ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql,
                                            final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability, final boolean returnGeneratedKeys) throws SQLException {
133 134 135
        if (Strings.isNullOrEmpty(sql)) {
            throw new SQLException(SQLExceptionConstant.SQL_STRING_NULL_OR_EMPTY);
        }
136
        this.connection = connection;
J
Juan Pan(Trista) 已提交
137
        schemaContexts = connection.getSchemaContexts();
T
terrymanu 已提交
138
        this.sql = sql;
139 140
        statements = new ArrayList<>();
        parameterSets = new ArrayList<>();
141
        ShardingSphereSQLParserEngine sqlStatementParserEngine = new ShardingSphereSQLParserEngine(DatabaseTypeRegistry.getTrunkDatabaseTypeName(schemaContexts.getDatabaseType()));
142
        sqlStatement = sqlStatementParserEngine.parse(sql, true);
143
        parameterMetaData = new ShardingSphereParameterMetaData(sqlStatement);
144
        statementOption = returnGeneratedKeys ? new StatementOption(true) : new StatementOption(resultSetType, resultSetConcurrency, resultSetHoldability);
L
Liang Zhang 已提交
145
        SQLExecutor sqlExecutor = new SQLExecutor(schemaContexts.getExecutorKernel(), connection.isHoldTransaction());
J
Juan Pan(Trista) 已提交
146
        preparedStatementExecutor = new PreparedStatementExecutor(connection.getDataSourceMap(), schemaContexts, sqlExecutor);
L
Liang Zhang 已提交
147
        rawExecutor = new RawJDBCExecutor(schemaContexts.getExecutorKernel(), connection.isHoldTransaction());
J
Juan Pan(Trista) 已提交
148
        batchPreparedStatementExecutor = new BatchPreparedStatementExecutor(schemaContexts, sqlExecutor);
149
        kernelProcessor = new KernelProcessor();
T
terrymanu 已提交
150 151
    }
    
T
terrymanu 已提交
152 153
    @Override
    public ResultSet executeQuery() throws SQLException {
T
terrymanu 已提交
154
        ResultSet result;
155
        try {
T
tristaZero 已提交
156
            clearPrevious();
157
            executionContext = createExecutionContext();
L
Liang Zhang 已提交
158 159 160 161 162 163 164
            List<QueryResult> queryResults;
            if (ExecutorConstant.MANAGED_RESOURCE) {
                Collection<InputGroup<StatementExecuteUnit>> inputGroups = getInputGroups();
                cacheStatements(inputGroups);
                reply();
                queryResults = preparedStatementExecutor.executeQuery(inputGroups);
            } else {
L
Liang Zhang 已提交
165
                queryResults = rawExecutor.executeQuery(getRawInputGroups(), new RawSQLExecutorCallback());
L
Liang Zhang 已提交
166 167
            }
            MergedResult mergedResult = mergeQuery(queryResults);
168
            result = new ShardingSphereResultSet(statements.stream().map(this::getResultSet).collect(Collectors.toList()), mergedResult, this, executionContext);
169
        } finally {
T
terrymanu 已提交
170
            clearBatch();
171
        }
172
        currentResultSet = result;
T
terrymanu 已提交
173
        return result;
T
terrymanu 已提交
174 175 176 177
    }
    
    @Override
    public int executeUpdate() throws SQLException {
178
        try {
T
tristaZero 已提交
179
            clearPrevious();
180
            executionContext = createExecutionContext();
L
Liang Zhang 已提交
181 182 183 184
            if (ExecutorConstant.MANAGED_RESOURCE) {
                Collection<InputGroup<StatementExecuteUnit>> inputGroups = getInputGroups();
                cacheStatements(inputGroups);
                reply();
185
                return preparedStatementExecutor.executeUpdate(inputGroups, executionContext.getSqlStatementContext(), executionContext.getRouteContext().getRouteUnits());
L
Liang Zhang 已提交
186
            } else {
L
Liang Zhang 已提交
187
                return rawExecutor.executeUpdate(getRawInputGroups(), new RawSQLExecutorCallback());
L
Liang Zhang 已提交
188
            }
189
        } finally {
T
terrymanu 已提交
190
            clearBatch();
191
        }
T
terrymanu 已提交
192 193 194 195
    }
    
    @Override
    public boolean execute() throws SQLException {
196
        try {
T
tristaZero 已提交
197
            clearPrevious();
198
            executionContext = createExecutionContext();
L
Liang Zhang 已提交
199 200 201 202
            if (ExecutorConstant.MANAGED_RESOURCE) {
                Collection<InputGroup<StatementExecuteUnit>> inputGroups = getInputGroups();
                cacheStatements(inputGroups);
                reply();
203
                return preparedStatementExecutor.execute(inputGroups, executionContext.getSqlStatementContext().getSqlStatement(), executionContext.getRouteContext().getRouteUnits());
L
Liang Zhang 已提交
204 205
            } else {
                // TODO process getStatement
L
Liang Zhang 已提交
206
                return rawExecutor.execute(getRawInputGroups(), new RawSQLExecutorCallback());
L
Liang Zhang 已提交
207
            }
208
        } finally {
T
terrymanu 已提交
209
            clearBatch();
210 211 212
        }
    }
    
L
Liang Zhang 已提交
213
    private Collection<InputGroup<StatementExecuteUnit>> getInputGroups() throws SQLException {
L
Liang Zhang 已提交
214
        int maxConnectionsSizePerQuery = schemaContexts.getProps().<Integer>getValue(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY);
215
        return new PreparedStatementExecuteGroupEngine(maxConnectionsSizePerQuery, connection, statementOption,
216
                schemaContexts.getDefaultMetaData().getRuleMetaData().getRules()).generate(executionContext.getRouteContext(), executionContext.getExecutionUnits());
L
Liang Zhang 已提交
217 218 219
    }
    
    private Collection<InputGroup<RawSQLExecuteUnit>> getRawInputGroups() throws SQLException {
L
Liang Zhang 已提交
220
        int maxConnectionsSizePerQuery = schemaContexts.getProps().<Integer>getValue(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY);
221
        return new RawExecuteGroupEngine(maxConnectionsSizePerQuery, schemaContexts.getDefaultMetaData().getRuleMetaData().getRules())
222
                .generate(executionContext.getRouteContext(), executionContext.getExecutionUnits());
L
Liang Zhang 已提交
223 224
    }
    
225 226 227 228 229 230 231 232
    @Override
    public ResultSet getResultSet() throws SQLException {
        if (null != currentResultSet) {
            return currentResultSet;
        }
        if (executionContext.getSqlStatementContext() instanceof SelectStatementContext || executionContext.getSqlStatementContext().getSqlStatement() instanceof DALStatement) {
            List<ResultSet> resultSets = getResultSets();
            MergedResult mergedResult = mergeQuery(getQueryResults(resultSets));
233
            currentResultSet = new ShardingSphereResultSet(resultSets, mergedResult, this, executionContext);
234 235 236 237
        }
        return currentResultSet;
    }
    
238 239 240 241 242 243 244 245
    private ResultSet getResultSet(final Statement statement) {
        try {
            return statement.getResultSet();
        } catch (final SQLException ex) {
            throw new ShardingSphereException(ex);
        }
    }
    
246
    private List<ResultSet> getResultSets() throws SQLException {
247 248
        List<ResultSet> result = new ArrayList<>(statements.size());
        for (Statement each : statements) {
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
            result.add(each.getResultSet());
        }
        return result;
    }
    
    private List<QueryResult> getQueryResults(final List<ResultSet> resultSets) throws SQLException {
        List<QueryResult> result = new ArrayList<>(resultSets.size());
        for (ResultSet each : resultSets) {
            if (null != each) {
                result.add(new StreamQueryResult(each));
            }
        }
        return result;
    }
    
264
    private ExecutionContext createExecutionContext() {
265
        LogicSQL logicSQL = createLogicSQL();
266
        ExecutionContext result = kernelProcessor.generateExecutionContext(logicSQL, schemaContexts.getDefaultMetaData(), schemaContexts.getProps());
267
        findGeneratedKey(result).ifPresent(generatedKey -> generatedValues.addAll(generatedKey.getGeneratedValues()));
268
        logSQL(logicSQL, result);
269
        return result;
270 271
    }
    
272
    private LogicSQL createLogicSQL() {
273
        List<Object> parameters = new ArrayList<>(getParameters());
274 275
        ShardingSphereSchema schema = schemaContexts.getDefaultMetaData().getSchema();
        SQLStatementContext<?> sqlStatementContext = SQLStatementContextFactory.newInstance(schema, parameters, sqlStatement);
L
Liang Zhang 已提交
276
        return new LogicSQL(sqlStatementContext, sql, parameters);
277 278
    }
    
279
    private MergedResult mergeQuery(final List<QueryResult> queryResults) throws SQLException {
280
        ShardingSphereMetaData metaData = schemaContexts.getDefaultMetaData();
281
        MergeEngine mergeEngine = new MergeEngine(schemaContexts.getDatabaseType(), metaData.getSchema(), schemaContexts.getProps(), metaData.getRuleMetaData().getRules());
282 283 284
        return mergeEngine.merge(queryResults, executionContext.getSqlStatementContext());
    }
    
285 286 287 288 289 290 291
    private void reply() {
        setParametersForStatements();
        replayMethodForStatements();
    }
    
    private void cacheStatements(final Collection<InputGroup<StatementExecuteUnit>> inputGroups) {
        for (InputGroup<StatementExecuteUnit> each : inputGroups) {
292
            statements.addAll(each.getInputs().stream().map(statementExecuteUnit -> (PreparedStatement) statementExecuteUnit.getStorageResource()).collect(Collectors.toList()));
293
            parameterSets.addAll(each.getInputs().stream().map(input -> input.getExecutionUnit().getSqlUnit().getParameters()).collect(Collectors.toList()));
T
terrymanu 已提交
294
        }
295 296 297 298 299
    }
    
    private void setParametersForStatements() {
        for (int i = 0; i < statements.size(); i++) {
            replaySetParameter(statements.get(i), parameterSets.get(i));
T
terrymanu 已提交
300 301 302
        }
    }
    
303 304
    private void replayMethodForStatements() {
        statements.forEach(this::replayMethodsInvocation);
T
terrymanu 已提交
305 306
    }
    
307 308 309
    private void clearPrevious() throws SQLException {
        clearStatements();
        parameterSets.clear();
T
tristaZero 已提交
310 311
    }
    
312 313 314
    private Optional<GeneratedKeyContext> findGeneratedKey(final ExecutionContext executionContext) {
        return executionContext.getSqlStatementContext() instanceof InsertStatementContext
                ? ((InsertStatementContext) executionContext.getSqlStatementContext()).getGeneratedKeyContext() : Optional.empty();
T
tristaZero 已提交
315 316
    }
    
317
    private void logSQL(final LogicSQL logicSQL, final ExecutionContext executionContext) {
L
Liang Zhang 已提交
318
        if (schemaContexts.getProps().<Boolean>getValue(ConfigurationPropertyKey.SQL_SHOW)) {
319
            SQLLogger.logSQL(logicSQL, schemaContexts.getProps().<Boolean>getValue(ConfigurationPropertyKey.SQL_SIMPLE), executionContext);
320 321 322
        }
    }
    
323 324 325 326 327 328 329 330 331 332
    @Override
    public ResultSet getGeneratedKeys() throws SQLException {
        Optional<GeneratedKeyContext> generatedKey = findGeneratedKey(executionContext);
        if (statementOption.isReturnGeneratedKeys() && generatedKey.isPresent()) {
            return new GeneratedKeysResultSet(generatedKey.get().getColumnName(), generatedValues.iterator(), this);
        }
        if (1 == statements.size()) {
            return statements.iterator().next().getGeneratedKeys();
        }
        return new GeneratedKeysResultSet();
T
tristaZero 已提交
333 334
    }
    
T
tristaZero 已提交
335
    @Override
T
tristaZero 已提交
336
    public void addBatch() {
T
tristaZero 已提交
337
        try {
338
            executionContext = createExecutionContext();
339
            batchPreparedStatementExecutor.addBatchForExecutionUnits(executionContext.getExecutionUnits());
T
tristaZero 已提交
340 341
        } finally {
            currentResultSet = null;
342
            clearParameters();
T
tristaZero 已提交
343 344
        }
    }
T
tristaZero 已提交
345
    
T
tristaZero 已提交
346 347 348
    @Override
    public int[] executeBatch() throws SQLException {
        try {
L
Liang Zhang 已提交
349
            // TODO add raw SQL executor
T
tristaZero 已提交
350
            initBatchPreparedStatementExecutor();
351
            return batchPreparedStatementExecutor.executeBatch(executionContext.getSqlStatementContext());
T
tristaZero 已提交
352 353 354 355 356
        } finally {
            clearBatch();
        }
    }
    
357
    private void initBatchPreparedStatementExecutor() throws SQLException {
358
        PreparedStatementExecuteGroupEngine executeGroupEngine = new PreparedStatementExecuteGroupEngine(
359
                schemaContexts.getProps().<Integer>getValue(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY),
360
                connection, statementOption, schemaContexts.getDefaultMetaData().getRuleMetaData().getRules());
361
        batchPreparedStatementExecutor.init(executeGroupEngine.generate(executionContext.getRouteContext(),
L
Liang Zhang 已提交
362
                new ArrayList<>(batchPreparedStatementExecutor.getBatchExecutionUnits()).stream().map(BatchExecutionUnit::getExecutionUnit).collect(Collectors.toList())));
T
tristaZero 已提交
363
        setBatchParametersForStatements();
364 365
    }
    
T
tristaZero 已提交
366
    private void setBatchParametersForStatements() throws SQLException {
367 368 369 370
        for (Statement each : batchPreparedStatementExecutor.getStatements()) {
            List<List<Object>> parameterSet = batchPreparedStatementExecutor.getParameterSet(each);
            for (List<Object> parameters : parameterSet) {
                replaySetParameter((PreparedStatement) each, parameters);
T
tristaZero 已提交
371
                ((PreparedStatement) each).addBatch();
T
tristaZero 已提交
372 373 374 375
            }
        }
    }
    
T
tristaZero 已提交
376
    @Override
T
tristaZero 已提交
377
    public void clearBatch() throws SQLException {
T
tristaZero 已提交
378
        currentResultSet = null;
T
tristaZero 已提交
379 380
        batchPreparedStatementExecutor.clear();
        clearParameters();
T
tristaZero 已提交
381 382
    }
    
T
terrymanu 已提交
383
    @SuppressWarnings("MagicConstant")
T
tristaZero 已提交
384 385
    @Override
    public int getResultSetType() {
386
        return statementOption.getResultSetType();
T
tristaZero 已提交
387 388
    }
    
T
terrymanu 已提交
389
    @SuppressWarnings("MagicConstant")
T
tristaZero 已提交
390 391
    @Override
    public int getResultSetConcurrency() {
392
        return statementOption.getResultSetConcurrency();
T
tristaZero 已提交
393 394 395 396
    }
    
    @Override
    public int getResultSetHoldability() {
397
        return statementOption.getResultSetHoldability();
T
tristaZero 已提交
398
    }
T
tristaZero 已提交
399
    
400 401
    @Override
    public boolean isAccumulate() {
402
        return schemaContexts.getDefaultMetaData().getRuleMetaData().getRules().stream().anyMatch(
403
            each -> each instanceof DataNodeContainedRule && ((DataNodeContainedRule) each).isNeedAccumulate(executionContext.getSqlStatementContext().getTablesContext().getTableNames()));
404 405
    }
    
T
tristaZero 已提交
406 407
    @Override
    public Collection<PreparedStatement> getRoutedStatements() {
408 409 410 411 412 413 414
        return statements;
    }
    
    private void clearStatements() throws SQLException {
        for (Statement each : statements) {
            each.close();
        }
T
terrymanu 已提交
415
        statements.clear();
T
tristaZero 已提交
416
    }
T
terrymanu 已提交
417
}