1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
81
82
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
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
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
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
200
201
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
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 }