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.connection;
19  
20  import lombok.Getter;
21  import org.apache.shardingsphere.driver.jdbc.adapter.AbstractConnectionAdapter;
22  import org.apache.shardingsphere.driver.jdbc.core.datasource.metadata.ShardingSphereDatabaseMetaData;
23  import org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement;
24  import org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSphereStatement;
25  import org.apache.shardingsphere.driver.exception.ConnectionClosedException;
26  import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
27  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
28  import org.apache.shardingsphere.infra.executor.sql.process.ProcessEngine;
29  import org.apache.shardingsphere.infra.metadata.user.Grantee;
30  import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
31  import org.apache.shardingsphere.mode.manager.ContextManager;
32  import org.apache.shardingsphere.transaction.api.TransactionType;
33  import org.apache.shardingsphere.transaction.rule.TransactionRule;
34  
35  import java.sql.Array;
36  import java.sql.CallableStatement;
37  import java.sql.DatabaseMetaData;
38  import java.sql.PreparedStatement;
39  import java.sql.SQLException;
40  import java.sql.SQLFeatureNotSupportedException;
41  import java.sql.Savepoint;
42  import java.sql.Statement;
43  
44  /**
45   * ShardingSphere connection.
46   */
47  @HighFrequencyInvocation
48  public final class ShardingSphereConnection extends AbstractConnectionAdapter {
49      
50      private final ProcessEngine processEngine = new ProcessEngine();
51      
52      @Getter
53      private final String databaseName;
54      
55      @Getter
56      private final ContextManager contextManager;
57      
58      @Getter
59      private final DriverDatabaseConnectionManager databaseConnectionManager;
60      
61      @Getter
62      private final String processId;
63      
64      private boolean autoCommit = true;
65      
66      private int transactionIsolation = TRANSACTION_READ_UNCOMMITTED;
67      
68      private boolean readOnly;
69      
70      private volatile boolean closed;
71      
72      public ShardingSphereConnection(final String databaseName, final ContextManager contextManager) {
73          this.databaseName = databaseName;
74          this.contextManager = contextManager;
75          databaseConnectionManager = new DriverDatabaseConnectionManager(databaseName, contextManager);
76          processId = processEngine.connect(new Grantee("", ""), databaseName);
77      }
78      
79      /**
80       * Whether hold transaction or not.
81       *
82       * @return true or false
83       */
84      public boolean isHoldTransaction() {
85          return databaseConnectionManager.getConnectionTransaction().isHoldTransaction(autoCommit);
86      }
87      
88      @Override
89      public DatabaseMetaData getMetaData() throws SQLException {
90          return new ShardingSphereDatabaseMetaData(this);
91      }
92      
93      @Override
94      public PreparedStatement prepareStatement(final String sql) throws SQLException {
95          return new ShardingSpherePreparedStatement(this, sql);
96      }
97      
98      @Override
99      public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
100         return new ShardingSpherePreparedStatement(this, sql, resultSetType, resultSetConcurrency);
101     }
102     
103     @Override
104     public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException {
105         return new ShardingSpherePreparedStatement(this, sql, resultSetType, resultSetConcurrency, resultSetHoldability);
106     }
107     
108     @Override
109     public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
110         return new ShardingSpherePreparedStatement(this, sql, autoGeneratedKeys);
111     }
112     
113     @Override
114     public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
115         return new ShardingSpherePreparedStatement(this, sql, Statement.RETURN_GENERATED_KEYS);
116     }
117     
118     @Override
119     public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
120         return new ShardingSpherePreparedStatement(this, sql, columnNames);
121     }
122     
123     @Override
124     public Statement createStatement() {
125         return new ShardingSphereStatement(this);
126     }
127     
128     @Override
129     public Statement createStatement(final int resultSetType, final int resultSetConcurrency) {
130         return new ShardingSphereStatement(this, resultSetType, resultSetConcurrency);
131     }
132     
133     @Override
134     public Statement createStatement(final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) {
135         return new ShardingSphereStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability);
136     }
137     
138     @Override
139     public CallableStatement prepareCall(final String sql) throws SQLException {
140         // TODO Support single DataSource scenario for now. Implement ShardingSphereCallableStatement to support multi DataSource scenarios.
141         return databaseConnectionManager.getRandomConnection().prepareCall(sql);
142     }
143     
144     @Override
145     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException {
146         // TODO Support single DataSource scenario for now. Implement ShardingSphereCallableStatement to support multi DataSource scenarios.
147         return databaseConnectionManager.getRandomConnection().prepareCall(sql, resultSetType, resultSetConcurrency);
148     }
149     
150     @Override
151     public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException {
152         // TODO Support single DataSource scenario for now. Implement ShardingSphereCallableStatement to support multi DataSource scenarios.
153         return databaseConnectionManager.getRandomConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
154     }
155     
156     @Override
157     public boolean getAutoCommit() {
158         return autoCommit;
159     }
160     
161     @Override
162     public void setAutoCommit(final boolean autoCommit) throws SQLException {
163         this.autoCommit = autoCommit;
164         if (databaseConnectionManager.getConnectionTransaction().isLocalTransaction()) {
165             processLocalTransaction();
166         } else {
167             processDistributedTransaction();
168         }
169     }
170     
171     private void processLocalTransaction() throws SQLException {
172         databaseConnectionManager.setAutoCommit(autoCommit);
173         if (!autoCommit) {
174             getConnectionContext().getTransactionContext()
175                     .beginTransaction(String.valueOf(contextManager.getMetaDataContexts().getMetaData().getGlobalRuleMetaData().getSingleRule(TransactionRule.class).getDefaultType()));
176         }
177     }
178     
179     private void processDistributedTransaction() throws SQLException {
180         switch (databaseConnectionManager.getConnectionTransaction().getDistributedTransactionOperationType(autoCommit)) {
181             case BEGIN:
182                 beginDistributedTransaction();
183                 break;
184             case COMMIT:
185                 databaseConnectionManager.getConnectionTransaction().commit();
186                 break;
187             default:
188                 break;
189         }
190     }
191     
192     private void beginDistributedTransaction() throws SQLException {
193         databaseConnectionManager.close();
194         databaseConnectionManager.getConnectionTransaction().begin();
195         getConnectionContext().getTransactionContext().beginTransaction(String.valueOf(databaseConnectionManager.getConnectionTransaction().getTransactionType()));
196     }
197     
198     /**
199      * Handle auto commit.
200      *
201      * @throws SQLException SQL exception
202      */
203     public void handleAutoCommit() throws SQLException {
204         if (!autoCommit && !databaseConnectionManager.getConnectionTransaction().isInTransaction()) {
205             if (TransactionType.isDistributedTransaction(databaseConnectionManager.getConnectionTransaction().getTransactionType())) {
206                 beginDistributedTransaction();
207             } else {
208                 if (!getConnectionContext().getTransactionContext().isInTransaction()) {
209                     getConnectionContext().getTransactionContext().beginTransaction(String.valueOf(databaseConnectionManager.getConnectionTransaction().getTransactionType()));
210                 }
211             }
212         }
213     }
214     
215     @Override
216     public void commit() throws SQLException {
217         try {
218             databaseConnectionManager.commit();
219         } finally {
220             databaseConnectionManager.getConnectionContext().getTransactionContext().setExceptionOccur(false);
221             getConnectionContext().close();
222         }
223     }
224     
225     @Override
226     public void rollback() throws SQLException {
227         try {
228             databaseConnectionManager.rollback();
229         } finally {
230             databaseConnectionManager.getConnectionContext().getTransactionContext().setExceptionOccur(false);
231             getConnectionContext().close();
232         }
233     }
234     
235     @Override
236     public void rollback(final Savepoint savepoint) throws SQLException {
237         checkClose();
238         databaseConnectionManager.rollback(savepoint);
239     }
240     
241     @Override
242     public Savepoint setSavepoint(final String name) throws SQLException {
243         checkClose();
244         if (!isHoldTransaction()) {
245             throw new SQLFeatureNotSupportedException("Savepoint can only be used in transaction blocks.");
246         }
247         return databaseConnectionManager.setSavepoint(name);
248     }
249     
250     @Override
251     public Savepoint setSavepoint() throws SQLException {
252         checkClose();
253         ShardingSpherePreconditions.checkState(isHoldTransaction(), () -> new SQLFeatureNotSupportedException("Savepoint can only be used in transaction blocks."));
254         return databaseConnectionManager.setSavepoint();
255     }
256     
257     @Override
258     public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
259         checkClose();
260         if (!isHoldTransaction()) {
261             return;
262         }
263         databaseConnectionManager.releaseSavepoint(savepoint);
264     }
265     
266     private void checkClose() throws SQLException {
267         ShardingSpherePreconditions.checkState(!isClosed(), () -> new ConnectionClosedException().toSQLException());
268     }
269     
270     @SuppressWarnings("MagicConstant")
271     @Override
272     public int getTransactionIsolation() throws SQLException {
273         return databaseConnectionManager.getTransactionIsolation().orElseGet(() -> transactionIsolation);
274     }
275     
276     @Override
277     public void setTransactionIsolation(final int level) throws SQLException {
278         transactionIsolation = level;
279         databaseConnectionManager.setTransactionIsolation(level);
280     }
281     
282     @Override
283     public boolean isReadOnly() {
284         return readOnly;
285     }
286     
287     @Override
288     public void setReadOnly(final boolean readOnly) throws SQLException {
289         this.readOnly = readOnly;
290         databaseConnectionManager.setReadOnly(readOnly);
291     }
292     
293     @Override
294     public boolean isValid(final int timeout) throws SQLException {
295         return databaseConnectionManager.isValid(timeout);
296     }
297     
298     @Override
299     public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
300         return databaseConnectionManager.getRandomConnection().createArrayOf(typeName, elements);
301     }
302     
303     @Override
304     public String getSchema() {
305         // TODO return databaseName for now in getSchema(), the same as before
306         return databaseName;
307     }
308     
309     @Override
310     public boolean isClosed() {
311         return closed;
312     }
313     
314     @Override
315     public void close() throws SQLException {
316         if (databaseConnectionManager.getConnectionTransaction().isInTransaction(databaseConnectionManager.getConnectionContext().getTransactionContext())) {
317             databaseConnectionManager.getConnectionTransaction().rollback();
318         }
319         closed = true;
320         databaseConnectionManager.close();
321         processEngine.disconnect(processId);
322     }
323     
324     private ConnectionContext getConnectionContext() {
325         return databaseConnectionManager.getConnectionContext();
326     }
327 }