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

future-architect / uroborosql / #743

28 Jun 2024 04:17PM UTC coverage: 90.988% (+0.5%) from 90.484%
#743

push

web-flow
add paramIfNotEmpty method (#318)

* v0.x to master merge

* fix nanoseconds diff (java8 to java11)

* eclipse cleanup and  optimize import

* eclipse format

* optimize Imports

* remove unused annotation

* library version up

* migrate v0.x to master
- update java version 8 to 11
- update junit4 to junit5
- library version update

* fix test failed

* remove unused annotation

* fixed bug

* use var

* fix java8 coding style

* Refactoring TransactionContextManager

* Refactoring SqlAgent

* 途中コミット

* fix testcase

* fix review

* fix javadoc comments

* merge v0.x PR

* cleanup code

* cleanup test code

* change build status badge

* - agent.query, update, proc, batch にそれぞれSupplierを引数にとるメソッドを追加
- SqlQuery.paramとSqlEntityUpdate.setにFunctionを受け取るメソッドを追加

* testcaseの整形

* - SqlFluent と ExtractionCondition の分離
- Arrays.asList -> List.of への変更
- テストの整形

* - v0.x系の不具合対応の追いつき
- ログ出力の整理

* - SqlKindの整理(ENTITY_XXXの追加)
- REPL_LOGの追加
- Deprecatedメソッドの削除(SqlAgent)
- SqlAgent, ExecutionContextでsetterをfluent APIに変更

* DB接続URLの修正

* add and fix testcases.

* add event testcases.

* fix typo

* add paramIfNotEmpty method and StringUtils rename ObjectUtils

* fix review comments.

* remove unused import

1695 of 1958 new or added lines in 97 files covered. (86.57%)

26 existing lines in 10 files now uncovered.

8249 of 9066 relevant lines covered (90.99%)

0.91 hits per line

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

93.14
/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 org.slf4j.Logger;
20
import org.slf4j.LoggerFactory;
21

22
import jp.co.future.uroborosql.config.SqlConfig;
23
import jp.co.future.uroborosql.connection.ConnectionContext;
24
import jp.co.future.uroborosql.context.ExecutionContext;
25
import jp.co.future.uroborosql.event.AfterBeginTransactionEvent;
26
import jp.co.future.uroborosql.event.BeforeEndTransactionEvent;
27
import jp.co.future.uroborosql.exception.UroborosqlSQLException;
28

29
/**
30
 * ローカルトランザクションマネージャ
31
 *
32
 * @author ota
33
 */
34
public class LocalTransactionManager implements TransactionManager {
35
        /** ロガー */
36
        private static final Logger LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.log");
1✔
37

38
        /** SQL設定クラス */
39
        private final SqlConfig sqlConfig;
40

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

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

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

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

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

65
        /**
66
         * {@inheritDoc}
67
         *
68
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.lang.Runnable)
69
         */
70
        @Override
71
        public void required(final Runnable runnable) {
72
                requiredInternal(toSupplier(runnable));
1✔
73
        }
1✔
74

75
        /**
76
         * {@inheritDoc}
77
         *
78
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.util.function.Supplier)
79
         */
80
        @Override
81
        public <R> R required(final Supplier<R> supplier) {
82
                return requiredInternal(supplier);
1✔
83
        }
84

85
        /**
86
         * {@inheritDoc}
87
         *
88
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(java.lang.Runnable)
89
         */
90
        @Override
91
        public void requiresNew(final Runnable runnable) {
92
                requiresNewInternal(toSupplier(runnable));
1✔
93
        }
1✔
94

95
        /**
96
         * {@inheritDoc}
97
         *
98
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(java.util.function.Supplier)
99
         */
100
        @Override
101
        public <R> R requiresNew(final Supplier<R> supplier) {
102
                return requiresNewInternal(supplier);
1✔
103
        }
104

105
        /**
106
         * {@inheritDoc}
107
         *
108
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.lang.Runnable)
109
         */
110
        @Override
111
        public void notSupported(final Runnable runnable) {
112
                notSupportedInternal(toSupplier(runnable));
1✔
113
        }
1✔
114

115
        /**
116
         * {@inheritDoc}
117
         *
118
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.util.function.Supplier)
119
         */
120
        @Override
121
        public <R> R notSupported(final Supplier<R> supplier) {
122
                return notSupportedInternal(supplier);
1✔
123
        }
124

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

141
        /**
142
         * {@inheritDoc}
143
         *
144
         * @see jp.co.future.uroborosql.tx.TransactionManager#setRollbackOnly()
145
         */
146
        @Override
147
        public void setRollbackOnly() {
148
                currentTxContext(false).ifPresent(TransactionContext::setRollbackOnly);
1✔
149
        }
1✔
150

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

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

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

181
        /**
182
         * {@inheritDoc}
183
         *
184
         * @see jp.co.future.uroborosql.connection.ConnectionManager#getConnection()
185
         */
186
        @Override
187
        public Connection getConnection() {
188
                try {
189
                        return currentTxContext(true).orElseThrow().getConnection();
1✔
UNCOV
190
                } catch (SQLException ex) {
×
NEW
191
                        LOG.error(ex.getMessage(), ex);
×
192
                        return null;
×
193
                }
194
        }
195

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

425
}
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