View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.driver.jdbc.core.statement;
19  
20  import lombok.EqualsAndHashCode;
21  import lombok.RequiredArgsConstructor;
22  import org.apache.shardingsphere.driver.jdbc.adapter.executor.ForceExecuteTemplate;
23  import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
24  import org.apache.shardingsphere.infra.executor.sql.context.ExecutionUnit;
25  import org.apache.shardingsphere.infra.executor.sql.execute.engine.ConnectionMode;
26  import org.apache.shardingsphere.infra.executor.sql.prepare.driver.jdbc.ExecutorJDBCStatementManager;
27  import org.apache.shardingsphere.infra.executor.sql.prepare.driver.jdbc.StatementOption;
28  
29  import java.sql.Connection;
30  import java.sql.PreparedStatement;
31  import java.sql.SQLException;
32  import java.sql.SQLFeatureNotSupportedException;
33  import java.sql.Statement;
34  import java.util.Map;
35  import java.util.Optional;
36  import java.util.concurrent.ConcurrentHashMap;
37  
38  /**
39   * Statement manager.
40   */
41  public final class StatementManager implements ExecutorJDBCStatementManager, AutoCloseable {
42      
43      private final Map<CacheKey, Statement> cachedStatements = new ConcurrentHashMap<>();
44      
45      private final ForceExecuteTemplate<Statement> forceExecuteTemplate = new ForceExecuteTemplate<>();
46      
47      @Override
48      public Statement createStorageResource(final Connection connection, final ConnectionMode connectionMode, final StatementOption option, final DatabaseType databaseType) throws SQLException {
49          return createStatement(connection, option);
50      }
51      
52      @Override
53      public Statement createStorageResource(final ExecutionUnit executionUnit, final Connection connection, final int connectionOffset,
54                                             final ConnectionMode connectionMode, final StatementOption option, final DatabaseType databaseType) throws SQLException {
55          CacheKey cacheKey = new CacheKey(executionUnit, connectionMode, connectionOffset);
56          Statement result = cachedStatements.get(cacheKey);
57          if (null == result || result.getConnection().isClosed() || result.isClosed()) {
58              Optional.ofNullable(result).ifPresent(optional -> cachedStatements.remove(cacheKey));
59              result = prepareStatement(executionUnit, connection, option);
60              cachedStatements.put(cacheKey, result);
61          }
62          return result;
63      }
64      
65      @SuppressWarnings("MagicConstant")
66      private Statement createStatement(final Connection connection, final StatementOption option) throws SQLException {
67          Statement result;
68          try {
69              result = connection.createStatement(option.getResultSetType(), option.getResultSetConcurrency(), option.getResultSetHoldability());
70          } catch (final SQLFeatureNotSupportedException ignore) {
71              result = connection.createStatement();
72          }
73          return result;
74      }
75      
76      private PreparedStatement prepareStatement(final ExecutionUnit executionUnit, final Connection connection, final StatementOption option) throws SQLException {
77          PreparedStatement result;
78          String sql = executionUnit.getSqlUnit().getSql();
79          if (option.isReturnGeneratedKeys()) {
80              result = null == option.getColumns() || 0 == option.getColumns().length
81                      ? connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)
82                      : connection.prepareStatement(sql, option.getColumns());
83          } else {
84              result = prepareStatement(connection, sql, option.getResultSetType(), option.getResultSetConcurrency(), option.getResultSetHoldability());
85          }
86          return result;
87      }
88      
89      private PreparedStatement prepareStatement(final Connection connection, final String sql, final int resultSetType, final int resultSetConcurrency,
90                                                 final int resultSetHoldability) throws SQLException {
91          PreparedStatement result;
92          try {
93              result = connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
94          } catch (final SQLFeatureNotSupportedException ignore) {
95              result = connection.prepareStatement(sql);
96          }
97          return result;
98      }
99      
100     @Override
101     public void close() throws SQLException {
102         try {
103             forceExecuteTemplate.execute(cachedStatements.values(), Statement::close);
104         } finally {
105             cachedStatements.clear();
106         }
107     }
108     
109     @RequiredArgsConstructor
110     @EqualsAndHashCode
111     private static final class CacheKey {
112         
113         private final ExecutionUnit executionUnit;
114         
115         private final ConnectionMode connectionMode;
116         
117         private final int connectionOffset;
118     }
119 }