• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

future-architect / uroborosql / #767

26 Aug 2024 05:08PM UTC coverage: 90.274% (-0.9%) from 91.21%
#767

push

HidekiSugimoto189
各Logger用のインタフェースを作成し必要なクラスがimplementsするように修正。そのうえでSLF4J 2.xのAPIを利用するように変更(性能改善)

360 of 568 new or added lines in 32 files covered. (63.38%)

9 existing lines in 7 files now uncovered.

8864 of 9819 relevant lines covered (90.27%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.38
/src/main/java/jp/co/future/uroborosql/tx/LocalTransactionManager.java
1
/**
2
 * Copyright (c) 2017-present, Future Corporation
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
package jp.co.future.uroborosql.tx;
8

9
import java.sql.CallableStatement;
10
import java.sql.Connection;
11
import java.sql.PreparedStatement;
12
import java.sql.SQLException;
13
import java.util.Deque;
14
import java.util.Optional;
15
import java.util.UUID;
16
import java.util.concurrent.ConcurrentLinkedDeque;
17
import java.util.function.Supplier;
18

19
import jp.co.future.uroborosql.config.SqlConfig;
20
import jp.co.future.uroborosql.connection.ConnectionContext;
21
import jp.co.future.uroborosql.context.ExecutionContext;
22
import jp.co.future.uroborosql.event.AfterBeginTransactionEvent;
23
import jp.co.future.uroborosql.event.BeforeEndTransactionEvent;
24
import jp.co.future.uroborosql.exception.UroborosqlRuntimeException;
25
import jp.co.future.uroborosql.exception.UroborosqlSQLException;
26

27
/**
28
 * ローカルトランザクションマネージャ
29
 *
30
 * @author ota
31
 */
32
public class LocalTransactionManager implements TransactionManager {
33
        /** SQL設定クラス */
34
        private final SqlConfig sqlConfig;
35

36
        /** トランザクションコンテキストのスタック */
37
        private final Deque<TransactionContext> txCtxStack = new ConcurrentLinkedDeque<>();
1✔
38

39
        /** トランザクション管理外の接続に利用する便宜上のトランザクション */
40
        private Optional<TransactionContext> unmanagedTransaction = Optional.empty();
1✔
41

42
        /** トランザクション内での更新を強制するかどうか */
43
        private final boolean updatable;
44

45
        /** DB接続情報. ConnectionSupplierで指定したデフォルトの接続情報を使用する場合は<code>null</code>を指定する */
46
        private final ConnectionContext connectionContext;
47

48
        /**
49
         * コンストラクタ
50
         *
51
         * @param sqlConfig SQL設定クラス
52
         * @param connectionContext DB接続情報
53
         */
54
        public LocalTransactionManager(final SqlConfig sqlConfig, final ConnectionContext connectionContext) {
1✔
55
                this.sqlConfig = sqlConfig;
1✔
56
                this.updatable = !sqlConfig.getSqlAgentProvider().isForceUpdateWithinTransaction();
1✔
57
                this.connectionContext = connectionContext;
1✔
58
        }
1✔
59

60
        /**
61
         * {@inheritDoc}
62
         *
63
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.lang.Runnable)
64
         */
65
        @Override
66
        public void required(final Runnable runnable) {
67
                requiredInternal(toSupplier(runnable));
1✔
68
        }
1✔
69

70
        /**
71
         * {@inheritDoc}
72
         *
73
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.util.function.Supplier)
74
         */
75
        @Override
76
        public <R> R required(final Supplier<R> supplier) {
77
                return requiredInternal(supplier);
1✔
78
        }
79

80
        /**
81
         * {@inheritDoc}
82
         *
83
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(java.lang.Runnable)
84
         */
85
        @Override
86
        public void requiresNew(final Runnable runnable) {
87
                requiresNewInternal(toSupplier(runnable));
1✔
88
        }
1✔
89

90
        /**
91
         * {@inheritDoc}
92
         *
93
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(java.util.function.Supplier)
94
         */
95
        @Override
96
        public <R> R requiresNew(final Supplier<R> supplier) {
97
                return requiresNewInternal(supplier);
1✔
98
        }
99

100
        /**
101
         * {@inheritDoc}
102
         *
103
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.lang.Runnable)
104
         */
105
        @Override
106
        public void notSupported(final Runnable runnable) {
107
                notSupportedInternal(toSupplier(runnable));
1✔
108
        }
1✔
109

110
        /**
111
         * {@inheritDoc}
112
         *
113
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.util.function.Supplier)
114
         */
115
        @Override
116
        public <R> R notSupported(final Supplier<R> supplier) {
117
                return notSupportedInternal(supplier);
1✔
118
        }
119

120
        /**
121
         * unmanagedTransactionも含め、現在有効なTransactionContextを取得する
122
         *
123
         * @param unmanagedCreate unmanagedTransactionが利用される場合に、unmanagedTransactionが未作成ならunmanagedTransactionを作成するかどうか。<code>true</code>なら作成する.
124
         * @return unmanagedTransactionも含め、現在有効なTransactionContext
125
         */
126
        private Optional<TransactionContext> currentTxContext(final boolean unmanagedCreate) {
127
                return Optional.ofNullable(this.txCtxStack.peek()).or(() -> {
1✔
128
                        if (unmanagedCreate && this.unmanagedTransaction.isEmpty()) {
1✔
129
                                this.unmanagedTransaction = Optional
1✔
130
                                                .of(new LocalTransactionContext(this.sqlConfig, this.updatable, this.connectionContext));
1✔
131
                        }
132
                        return this.unmanagedTransaction;
1✔
133
                });
134
        }
135

136
        /**
137
         * {@inheritDoc}
138
         *
139
         * @see jp.co.future.uroborosql.tx.TransactionManager#setRollbackOnly()
140
         */
141
        @Override
142
        public void setRollbackOnly() {
143
                currentTxContext(false).ifPresent(TransactionContext::setRollbackOnly);
1✔
144
        }
1✔
145

146
        /**
147
         * {@inheritDoc}
148
         *
149
         * @see jp.co.future.uroborosql.tx.TransactionManager#setSavepoint(java.lang.String)
150
         */
151
        @Override
152
        public void setSavepoint(final String savepointName) {
153
                currentTxContext(false).ifPresent(txCtx -> txCtx.setSavepoint(savepointName));
1✔
154
        }
1✔
155

156
        /**
157
         * {@inheritDoc}
158
         *
159
         * @see jp.co.future.uroborosql.tx.TransactionManager#releaseSavepoint(java.lang.String)
160
         */
161
        @Override
162
        public void releaseSavepoint(final String savepointName) {
163
                currentTxContext(false).ifPresent(txCtx -> txCtx.releaseSavepoint(savepointName));
1✔
164
        }
1✔
165

166
        /**
167
         * {@inheritDoc}
168
         *
169
         * @see jp.co.future.uroborosql.tx.TransactionManager#rollback(java.lang.String)
170
         */
171
        @Override
172
        public void rollback(final String savepointName) {
173
                currentTxContext(false).ifPresent(txCtx -> txCtx.rollback(savepointName));
1✔
174
        }
1✔
175

176
        /**
177
         * {@inheritDoc}
178
         *
179
         * @see jp.co.future.uroborosql.connection.ConnectionManager#getConnection()
180
         */
181
        @Override
182
        public Connection getConnection() {
183
                try {
184
                        return currentTxContext(true).orElseThrow().getConnection();
1✔
185
                } catch (SQLException ex) {
×
NEW
186
                        LOG.atError()
×
NEW
187
                                        .setMessage(ex.getMessage())
×
NEW
188
                                        .setCause(ex)
×
NEW
189
                                        .log();
×
UNCOV
190
                        return null;
×
191
                }
192
        }
193

194
        /**
195
         * ステートメント初期化。
196
         *
197
         * @param executionContext ExecutionContext
198
         * @return PreparedStatement
199
         * @throws SQLException SQL例外
200
         */
201
        public PreparedStatement getPreparedStatement(final ExecutionContext executionContext) throws SQLException {
202
                return currentTxContext(true).orElseThrow().getPreparedStatement(executionContext);
1✔
203
        }
204

205
        /**
206
         * Callableステートメント初期化
207
         *
208
         * @param executionContext ExecutionContext
209
         * @return CallableStatement
210
         * @throws SQLException SQL例外
211
         */
212
        public CallableStatement getCallableStatement(final ExecutionContext executionContext) throws SQLException {
213
                return currentTxContext(true).orElseThrow().getCallableStatement(executionContext);
1✔
214
        }
215

216
        /**
217
         * 処理ブロックの実行
218
         *
219
         * @param runnable 処理ブロック
220
         * @return サプライヤ
221
         */
222
        private Supplier<Void> toSupplier(final Runnable runnable) {
223
                return () -> {
1✔
224
                        runnable.run();
1✔
225
                        return null;
1✔
226
                };
227
        }
228

229
        /**
230
         * requiredメソッドの内部実装
231
         *
232
         * @param supplier サプライヤ
233
         * @return 実行結果
234
         * @throws SQLException SQL例外
235
         */
236
        private <R> R requiredInternal(final Supplier<R> supplier) {
237
                if (!this.txCtxStack.isEmpty()) {
1✔
238
                        return supplier.get();
1✔
239
                } else {
240
                        return runInNewTx(supplier, false);
1✔
241
                }
242
        }
243

244
        /**
245
         * requiresNewメソッドの内部実装
246
         *
247
         * @param supplier サプライヤ
248
         * @return 実行結果
249
         * @throws SQLException SQL例外
250
         */
251
        private <R> R requiresNewInternal(final Supplier<R> supplier) {
252
                return runInNewTx(supplier, true);
1✔
253
        }
254

255
        /**
256
         * notSupportedメソッドの内部実装
257
         *
258
         * @param supplier サプライヤ
259
         * @return 実行結果
260
         */
261
        private <R> R notSupportedInternal(final Supplier<R> supplier) {
262
                if (!this.txCtxStack.isEmpty()) {
1✔
263
                        // トランザクションをサスペンド
264
                        var txCtx = this.txCtxStack.pop();
1✔
265
                        try {
266
                                return supplier.get();
1✔
267
                        } finally {
268
                                //戻す
269
                                this.txCtxStack.push(txCtx);
1✔
270
                        }
271
                } else {
272
                        return supplier.get();
1✔
273
                }
274
        }
275

276
        /**
277
         * 新しいトランザクションを開始して処理を実行
278
         *
279
         * @param supplier トランザクション内で実行する処理
280
         * @param isRequiredNew 新規トランザクションかどうか
281
         * @param <R> 結果の型
282
         * @return 処理の結果
283
         */
284
        private <R> R runInNewTx(final Supplier<R> supplier, final boolean isRequiredNew) {
285
                try (var txCtx = new LocalTransactionContext(this.sqlConfig, true, this.connectionContext)) {
1✔
286
                        this.txCtxStack.push(txCtx);
1✔
287
                        try {
288
                                // トランザクション開始後イベント発行
289
                                if (this.sqlConfig.getEventListenerHolder().hasAfterBeginTransactionListener()) {
1✔
290
                                        var eventObj = new AfterBeginTransactionEvent(txCtx, isRequiredNew, txCtxStack.size());
1✔
291
                                        this.sqlConfig.getEventListenerHolder().getAfterBeginTransactionListeners()
1✔
292
                                                        .forEach(listener -> listener.accept(eventObj));
1✔
293
                                }
294
                                R result = null;
1✔
295
                                try {
296
                                        result = supplier.get();
1✔
297
                                        return result;
1✔
298
                                } finally {
299
                                        // トランザクション終了前イベント発行
300
                                        if (this.sqlConfig.getEventListenerHolder().hasBeforeEndTransactionListener()) {
1✔
301
                                                var eventObj = new BeforeEndTransactionEvent(txCtx, isRequiredNew, txCtxStack.size(), result);
1✔
302
                                                this.sqlConfig.getEventListenerHolder().getBeforeEndTransactionListeners()
1✔
303
                                                                .forEach(listener -> listener.accept(eventObj));
1✔
304
                                        }
305
                                }
306
                        } catch (Throwable th) {
1✔
307
                                txCtx.setRollbackOnly();
1✔
308
                                throw th;
1✔
309
                        }
310
                } finally {
311
                        this.txCtxStack.pop();
1✔
312
                }
313
        }
314

315
        /**
316
         * {@inheritDoc}
317
         *
318
         * @see jp.co.future.uroborosql.connection.ConnectionManager#close()
319
         */
320
        @Override
321
        public void close() {
322
                if (!this.txCtxStack.isEmpty()) {
1✔
323
                        this.txCtxStack.forEach(TransactionContext::close);
×
324
                        this.txCtxStack.clear();
×
325
                }
326
                this.unmanagedTransaction.ifPresent(TransactionContext::close);
1✔
327
        }
1✔
328

329
        /**
330
         * {@inheritDoc}
331
         *
332
         * @see jp.co.future.uroborosql.connection.ConnectionManager#commit()
333
         */
334
        @Override
335
        public void commit() {
336
                currentTxContext(false).ifPresent(TransactionContext::commit);
1✔
337
        }
1✔
338

339
        /**
340
         * {@inheritDoc}
341
         *
342
         * @see jp.co.future.uroborosql.connection.ConnectionManager#rollback()
343
         */
344
        @Override
345
        public void rollback() {
346
                currentTxContext(false).ifPresent(TransactionContext::rollback);
1✔
347
        }
1✔
348

349
        /**
350
         * {@inheritDoc}
351
         *
352
         * @see jp.co.future.uroborosql.tx.TransactionManager#savepointScope(java.util.function.Supplier)
353
         */
354
        @Override
355
        public <R> R savepointScope(final Supplier<R> supplier) {
356
                var txCtx = currentTxContext(false)
1✔
357
                                .orElseThrow(() -> new UroborosqlRuntimeException("UnmanagedConnection cannot use savepoint."));
1✔
358
                var savepointName = UUID.randomUUID().toString();
1✔
359
                txCtx.setSavepoint(savepointName);
1✔
360
                try {
361
                        return supplier.get();
1✔
362
                } catch (Throwable th) {
1✔
363
                        txCtx.rollback(savepointName);
1✔
364
                        throw th;
1✔
365
                } finally {
366
                        txCtx.releaseSavepoint(savepointName);
1✔
367
                }
368
        }
369

370
        /**
371
         * {@inheritDoc}
372
         *
373
         * @see jp.co.future.uroborosql.tx.TransactionManager#savepointScope(java.lang.Runnable)
374
         */
375
        @Override
376
        public void savepointScope(final Runnable runnable) {
377
                savepointScope(toSupplier(runnable));
1✔
378
        }
1✔
379

380
        /**
381
         * {@inheritDoc}
382
         *
383
         * @see jp.co.future.uroborosql.tx.TransactionManager#autoCommitScope(java.util.function.Supplier)
384
         */
385
        @Override
386
        public <R> R autoCommitScope(final Supplier<R> supplier) {
387
                try (var txCtx = new LocalTransactionContext(this.sqlConfig, true, this.connectionContext)) {
1✔
388
                        this.txCtxStack.push(txCtx);
1✔
389

390
                        var conn = txCtx.getConnection();
1✔
391
                        var preserveAutoCommitState = conn.getAutoCommit();
1✔
392
                        try {
393
                                // autoCommit=trueに設定する
394
                                if (!preserveAutoCommitState) {
1✔
395
                                        conn.setAutoCommit(true);
1✔
396
                                }
397
                                return supplier.get();
1✔
398
                        } catch (Throwable th) {
1✔
399
                                txCtx.setRollbackOnly();
1✔
400
                                throw th;
1✔
401
                        } finally {
402
                                if (!preserveAutoCommitState) {
1✔
403
                                        conn.setAutoCommit(preserveAutoCommitState);
1✔
404
                                }
405
                        }
406
                } catch (SQLException se) {
×
407
                        throw new UroborosqlSQLException(se);
×
408
                } finally {
409
                        this.txCtxStack.pop().close();
1✔
410
                }
411
        }
412

413
        /**
414
         * {@inheritDoc}
415
         *
416
         * @see jp.co.future.uroborosql.tx.TransactionManager#autoCommitScope(java.lang.Runnable)
417
         */
418
        @Override
419
        public void autoCommitScope(final Runnable runnable) {
420
                autoCommitScope(toSupplier(runnable));
1✔
421
        }
1✔
422

423
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc