• 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

90.16
/src/main/java/jp/co/future/uroborosql/SqlAgentImpl.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;
8

9
import java.math.BigDecimal;
10
import java.math.BigInteger;
11
import java.sql.CallableStatement;
12
import java.sql.Connection;
13
import java.sql.PreparedStatement;
14
import java.sql.ResultSet;
15
import java.sql.SQLException;
16
import java.sql.Statement;
17
import java.time.Duration;
18
import java.time.Instant;
19
import java.time.LocalTime;
20
import java.time.format.DateTimeFormatter;
21
import java.util.ArrayList;
22
import java.util.Arrays;
23
import java.util.Collections;
24
import java.util.Comparator;
25
import java.util.HashMap;
26
import java.util.List;
27
import java.util.Map;
28
import java.util.Objects;
29
import java.util.Optional;
30
import java.util.Spliterator;
31
import java.util.Spliterators;
32
import java.util.concurrent.atomic.AtomicReference;
33
import java.util.function.Consumer;
34
import java.util.function.Supplier;
35
import java.util.stream.Collectors;
36
import java.util.stream.Stream;
37
import java.util.stream.StreamSupport;
38

39
import org.slf4j.Logger;
40
import org.slf4j.LoggerFactory;
41
import org.slf4j.MDC;
42

43
import jp.co.future.uroborosql.client.SqlParamUtils;
44
import jp.co.future.uroborosql.config.SqlConfig;
45
import jp.co.future.uroborosql.connection.ConnectionContext;
46
import jp.co.future.uroborosql.context.ExecutionContext;
47
import jp.co.future.uroborosql.context.ExecutionContextImpl;
48
import jp.co.future.uroborosql.converter.MapResultSetConverter;
49
import jp.co.future.uroborosql.converter.ResultSetConverter;
50
import jp.co.future.uroborosql.coverage.CoverageData;
51
import jp.co.future.uroborosql.coverage.CoverageHandler;
52
import jp.co.future.uroborosql.dialect.Dialect;
53
import jp.co.future.uroborosql.enums.InsertsType;
54
import jp.co.future.uroborosql.enums.SqlKind;
55
import jp.co.future.uroborosql.event.BeforeParseSqlEvent;
56
import jp.co.future.uroborosql.event.ProcedureEvent;
57
import jp.co.future.uroborosql.event.SqlBatchEvent;
58
import jp.co.future.uroborosql.event.SqlQueryEvent;
59
import jp.co.future.uroborosql.event.SqlUpdateEvent;
60
import jp.co.future.uroborosql.event.TransformSqlEvent;
61
import jp.co.future.uroborosql.exception.EntitySqlRuntimeException;
62
import jp.co.future.uroborosql.exception.OptimisticLockException;
63
import jp.co.future.uroborosql.exception.PessimisticLockException;
64
import jp.co.future.uroborosql.exception.UroborosqlRuntimeException;
65
import jp.co.future.uroborosql.exception.UroborosqlSQLException;
66
import jp.co.future.uroborosql.fluent.Procedure;
67
import jp.co.future.uroborosql.fluent.SqlBatch;
68
import jp.co.future.uroborosql.fluent.SqlEntityDelete;
69
import jp.co.future.uroborosql.fluent.SqlEntityQuery;
70
import jp.co.future.uroborosql.fluent.SqlEntityUpdate;
71
import jp.co.future.uroborosql.fluent.SqlQuery;
72
import jp.co.future.uroborosql.fluent.SqlUpdate;
73
import jp.co.future.uroborosql.mapping.EntityHandler;
74
import jp.co.future.uroborosql.mapping.MappingColumn;
75
import jp.co.future.uroborosql.mapping.MappingUtils;
76
import jp.co.future.uroborosql.mapping.TableMetadata;
77
import jp.co.future.uroborosql.parameter.Parameter;
78
import jp.co.future.uroborosql.parser.SqlParserImpl;
79
import jp.co.future.uroborosql.store.SqlResourceManager;
80
import jp.co.future.uroborosql.tx.LocalTransactionManager;
81
import jp.co.future.uroborosql.tx.TransactionManager;
82
import jp.co.future.uroborosql.utils.CaseFormat;
83
import jp.co.future.uroborosql.utils.ObjectUtils;
84

85
/**
86
 * SQL実行用クラス。
87
 *
88
 * @author H.Sugimoto
89
 */
90
public class SqlAgentImpl implements SqlAgent {
91
        /** ロガー */
92
        private static final Logger LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.log");
1✔
93

94
        /** SQLロガー */
95
        private static final Logger SQL_LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.sql");
1✔
96

97
        /** SQLカバレッジ用ロガー */
98
        private static final Logger COVERAGE_LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.sql.coverage");
1✔
99

100
        /** パフォーマンスロガー */
101
        private static final Logger PERFORMANCE_LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.performance");
1✔
102

103
        /** REPLロガー */
104
        private static final Logger REPL_LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.repl");
1✔
105

106
        /** ログ出力を抑止するためのMDCキー */
107
        private static final String SUPPRESS_PARAMETER_LOG_OUTPUT = "SuppressParameterLogOutput";
108

109
        /** ExecutionContext属性キー:リトライカウント */
110
        private static final String CTX_ATTR_KEY_RETRY_COUNT = "__retryCount";
111

112
        /** ExecutionContext属性キー:バインドパラメータコメントの出力有無 */
113
        private static final String CTX_ATTR_KEY_OUTPUT_BIND_COMMENT = "__outputBindComment";
114

115
        /** 例外発生にロールバックが必要なDBでリトライを実現するために設定するSavepointの名前 */
116
        private static final String RETRY_SAVEPOINT_NAME = "__retry_savepoint";
117

118
        /** IN句に渡すパラメータのMAXサイズ */
119
        private static final int IN_CLAUSE_MAX_PARAM_SIZE = 1000;
120

121
        /** 経過時間のフォーマッタ */
122
        private static final DateTimeFormatter ELAPSED_TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSS");
1✔
123

124
        /** カバレッジハンドラ */
125
        private static final AtomicReference<CoverageHandler> COVERAGE_HANDLER_REF = new AtomicReference<>();
1✔
126

127
        /** SQL設定管理クラス */
128
        private final SqlConfig sqlConfig;
129

130
        /** トランザクション管理機能 */
131
        private final TransactionManager transactionManager;
132

133
        /** 例外発生時のログ出力を行うかどうか デフォルトは<code>true</code> */
134
        private final boolean outputExceptionLog;
135

136
        /** クエリータイムアウト制限値 */
137
        private int queryTimeout = -1;
1✔
138

139
        /** フェッチサイズ */
140
        private int fetchSize = -1;
1✔
141

142
        /** SQL実行エラー時にリトライするエラーコードのリスト */
143
        private List<String> sqlRetryCodes = List.of();
1✔
144

145
        /** SQL実行エラー時の最大リトライ回数 */
146
        private int maxRetryCount = 0;
1✔
147

148
        /** SQL実行リトライ時の待機時間(ms) */
149
        private int retryWaitTime = 0;
1✔
150

151
        /** SQLを特定するための一意なIDに置換するためのキー */
152
        private final String keySqlId;
153

154
        /** Queryの結果を格納するMapのキーを生成する際に使用するCaseFormat */
155
        private CaseFormat mapKeyCaseFormat = CaseFormat.UPPER_SNAKE_CASE;
1✔
156

157
        /** {@link InsertsType} */
158
        private InsertsType insertsType = InsertsType.BATCH;
1✔
159

160
        static {
161
                // SQLカバレッジ取得用のクラス名を設定する。指定がない場合、またはfalseが指定された場合はカバレッジを収集しない。
162
                // クラス名が指定されている場合はそのクラス名を指定
163
                var sqlCoverageClassName = System.getProperty(KEY_SQL_COVERAGE);
1✔
164
                if (sqlCoverageClassName == null) {
1✔
NEW
165
                        COVERAGE_LOG.info("system property - uroborosql.sql.coverage not set. sql coverage turned off.");
×
166
                } else if (Boolean.FALSE.toString().equalsIgnoreCase(sqlCoverageClassName)) {
1✔
NEW
167
                        sqlCoverageClassName = null;
×
NEW
168
                        COVERAGE_LOG.info("system property - uroborosql.sql.coverage is set to false. sql coverage turned off.");
×
169
                } else if (Boolean.TRUE.toString().equalsIgnoreCase(sqlCoverageClassName)) {
1✔
170
                        // trueの場合は、デフォルト値を設定
171
                        sqlCoverageClassName = "jp.co.future.uroborosql.coverage.CoberturaCoverageHandler";
1✔
172
                        COVERAGE_LOG.info("system property - uroborosql.sql.coverage is set to true. sql coverage turned on.");
1✔
173
                }
174

175
                CoverageHandler handler = null;
1✔
176
                if (sqlCoverageClassName != null) {
1✔
177
                        try {
178
                                handler = (CoverageHandler) Class.forName(sqlCoverageClassName, true,
1✔
179
                                                Thread.currentThread().getContextClassLoader()).getConstructor().newInstance();
1✔
180
                                COVERAGE_LOG.info("CoverageHandler : {}", sqlCoverageClassName);
1✔
NEW
181
                        } catch (Exception ex) {
×
NEW
182
                                COVERAGE_LOG.warn("Failed to instantiate CoverageHandler class. Class:{}, Cause:{}",
×
183
                                                sqlCoverageClassName,
NEW
184
                                                ex.getMessage());
×
NEW
185
                                COVERAGE_LOG.info("Turn off sql coverage due to failure to instantiate CoverageHandler class.");
×
186
                        }
1✔
187
                }
188

189
                if (handler != null) {
1✔
190
                        COVERAGE_HANDLER_REF.set(handler);
1✔
191
                }
192
        }
1✔
193

194
        /**
195
         * コンストラクタ。
196
         *
197
         * @param sqlConfig SQL設定管理クラス
198
         * @param settings 設定情報
199
         * @param connectionContext DB接続情報
200
         */
201
        SqlAgentImpl(final SqlConfig sqlConfig, final Map<String, String> settings,
202
                        final ConnectionContext connectionContext) {
1✔
203
                this.sqlConfig = sqlConfig;
1✔
204
                this.transactionManager = new LocalTransactionManager(sqlConfig, connectionContext);
1✔
205

206
                // デフォルトプロパティ設定
207
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_FETCH_SIZE)) {
1✔
208
                        this.fetchSize = Integer.parseInt(settings.get(SqlAgentProvider.PROPS_KEY_FETCH_SIZE));
1✔
209
                }
210
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_QUERY_TIMEOUT)) {
1✔
211
                        this.queryTimeout = Integer.parseInt(settings.get(SqlAgentProvider.PROPS_KEY_QUERY_TIMEOUT));
1✔
212
                }
213
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_SQL_RETRY_CODES)) {
1✔
214
                        this.sqlRetryCodes = Collections.unmodifiableList(List.of(settings.get(
1✔
215
                                        SqlAgentProvider.PROPS_KEY_SQL_RETRY_CODES).split(",")));
1✔
216
                }
217
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_DEFAULT_MAX_RETRY_COUNT)) {
1✔
218
                        this.maxRetryCount = Integer.parseInt(settings.get(SqlAgentProvider.PROPS_KEY_DEFAULT_MAX_RETRY_COUNT));
1✔
219
                }
220
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_DEFAULT_SQL_RETRY_WAIT_TIME)) {
1✔
221
                        this.retryWaitTime = Integer.parseInt(settings
1✔
222
                                        .get(SqlAgentProvider.PROPS_KEY_DEFAULT_SQL_RETRY_WAIT_TIME));
1✔
223
                }
224
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_SQL_ID_KEY_NAME)) {
1✔
225
                        this.keySqlId = settings.get(SqlAgentProvider.PROPS_KEY_SQL_ID_KEY_NAME);
1✔
226
                } else {
227
                        this.keySqlId = "_SQL_ID_";
1✔
228
                }
229
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_DEFAULT_MAP_KEY_CASE_FORMAT)) {
1✔
230
                        this.mapKeyCaseFormat = CaseFormat.valueOf(settings
1✔
231
                                        .get(SqlAgentProvider.PROPS_KEY_DEFAULT_MAP_KEY_CASE_FORMAT));
1✔
232
                }
233
                if (settings.containsKey(SqlAgentProvider.PROPS_KEY_DEFAULT_INSERTS_TYPE)) {
1✔
234
                        this.insertsType = InsertsType.valueOf(settings
1✔
235
                                        .get(SqlAgentProvider.PROPS_KEY_DEFAULT_INSERTS_TYPE));
1✔
236
                }
237
                if (settings.containsKey(SqlAgentProviderImpl.PROPS_KEY_OUTPUT_EXCEPTION_LOG)) {
1✔
238
                        outputExceptionLog = Boolean
1✔
239
                                        .parseBoolean(settings.get(SqlAgentProviderImpl.PROPS_KEY_OUTPUT_EXCEPTION_LOG));
1✔
240
                } else {
NEW
241
                        outputExceptionLog = true;
×
242
                }
243
        }
1✔
244

245
        /**
246
         * {@inheritDoc}
247
         *
248
         * @see jp.co.future.uroborosql.SqlAgent#close()
249
         */
250
        @Override
251
        public void close() {
252
                transactionManager.close();
1✔
253
                if (COVERAGE_HANDLER_REF.get() != null) {
1✔
254
                        COVERAGE_HANDLER_REF.get().onSqlAgentClose();
1✔
255
                }
256
        }
1✔
257

258
        /**
259
         *
260
         * {@inheritDoc}
261
         *
262
         * @see jp.co.future.uroborosql.connection.ConnectionManager#getConnection()
263
         */
264
        @Override
265
        public Connection getConnection() {
266
                return transactionManager.getConnection();
1✔
267
        }
268

269
        /**
270
         *
271
         * {@inheritDoc}
272
         *
273
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.lang.Runnable)
274
         */
275
        @Override
276
        public void required(final Runnable runnable) {
277
                transactionManager.required(runnable);
1✔
278
        }
1✔
279

280
        /**
281
         *
282
         * {@inheritDoc}
283
         *
284
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.util.function.Supplier)
285
         */
286
        @Override
287
        public <R> R required(final Supplier<R> supplier) {
288
                return transactionManager.required(supplier);
1✔
289
        }
290

291
        /**
292
         * {@inheritDoc}
293
         *
294
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(jp.co.future.uroborosql.tx.Runnable)
295
         */
296
        @Override
297
        public void requiresNew(final Runnable runnable) {
298
                transactionManager.requiresNew(runnable);
1✔
299
        }
1✔
300

301
        /**
302
         *
303
         * {@inheritDoc}
304
         *
305
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(java.util.function.Supplier)
306
         */
307
        @Override
308
        public <R> R requiresNew(final Supplier<R> supplier) {
309
                return transactionManager.requiresNew(supplier);
1✔
310
        }
311

312
        /**
313
         *
314
         * {@inheritDoc}
315
         *
316
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.lang.Runnable)
317
         */
318
        @Override
319
        public void notSupported(final Runnable runnable) {
320
                transactionManager.notSupported(runnable);
1✔
321
        }
1✔
322

323
        /**
324
         *
325
         * {@inheritDoc}
326
         *
327
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.util.function.Supplier)
328
         */
329
        @Override
330
        public <R> R notSupported(final Supplier<R> supplier) {
331
                return transactionManager.notSupported(supplier);
1✔
332
        }
333

334
        /**
335
         *
336
         * {@inheritDoc}
337
         *
338
         * @see jp.co.future.uroborosql.tx.TransactionManager#setRollbackOnly()
339
         */
340
        @Override
341
        public void setRollbackOnly() {
342
                transactionManager.setRollbackOnly();
1✔
343
        }
1✔
344

345
        /**
346
         *
347
         * {@inheritDoc}
348
         *
349
         * @see jp.co.future.uroborosql.tx.TransactionManager#setSavepoint(java.lang.String)
350
         */
351
        @Override
352
        public void setSavepoint(final String savepointName) {
353
                transactionManager.setSavepoint(savepointName);
1✔
354
        }
1✔
355

356
        /**
357
         *
358
         * {@inheritDoc}
359
         *
360
         * @see jp.co.future.uroborosql.tx.TransactionManager#releaseSavepoint(java.lang.String)
361
         */
362
        @Override
363
        public void releaseSavepoint(final String savepointName) {
364
                transactionManager.releaseSavepoint(savepointName);
1✔
365
        }
1✔
366

367
        /**
368
         *
369
         * {@inheritDoc}
370
         *
371
         * @see jp.co.future.uroborosql.tx.TransactionManager#rollback(java.lang.String)
372
         */
373
        @Override
374
        public void rollback(final String savepointName) {
375
                transactionManager.rollback(savepointName);
1✔
376
        }
1✔
377

378
        /**
379
         *
380
         * {@inheritDoc}
381
         *
382
         * @see jp.co.future.uroborosql.tx.TransactionManager#savepointScope(java.util.function.Supplier)
383
         */
384
        @Override
385
        public <R> R savepointScope(final Supplier<R> supplier) {
386
                return transactionManager.savepointScope(supplier);
1✔
387
        }
388

389
        /**
390
         *
391
         * {@inheritDoc}
392
         *
393
         * @see jp.co.future.uroborosql.tx.TransactionManager#savepointScope(java.lang.Runnable)
394
         */
395
        @Override
396
        public void savepointScope(final Runnable runnable) {
397
                transactionManager.savepointScope(runnable);
1✔
398
        }
1✔
399

400
        /**
401
         *
402
         * {@inheritDoc}
403
         *
404
         * @see jp.co.future.uroborosql.tx.TransactionManager#autoCommitScope(java.util.function.Supplier)
405
         */
406
        @Override
407
        public <R> R autoCommitScope(final Supplier<R> supplier) {
408
                return transactionManager.autoCommitScope(supplier);
1✔
409
        }
410

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

422
        /**
423
         *
424
         * {@inheritDoc}
425
         *
426
         * @see jp.co.future.uroborosql.connection.ConnectionManager#commit()
427
         */
428
        @Override
429
        public void commit() {
430
                transactionManager.commit();
1✔
431
        }
1✔
432

433
        /**
434
         *
435
         * {@inheritDoc}
436
         *
437
         * @see jp.co.future.uroborosql.connection.ConnectionManager#rollback()
438
         */
439
        @Override
440
        public void rollback() {
441
                transactionManager.rollback();
1✔
442
        }
1✔
443

444
        /**
445
         * {@inheritDoc}
446
         *
447
         * @see jp.co.future.uroborosql.SqlAgent#query(java.lang.String)
448
         */
449
        @Override
450
        public SqlQuery query(final String sqlName) {
451
                if ("".equals(sqlName)) {
1✔
452
                        throw new IllegalArgumentException("sqlName is required.");
1✔
453
                }
454
                return new SqlQueryImpl(this, context().setSqlName(sqlName));
1✔
455
        }
456

457
        /**
458
         * {@inheritDoc}
459
         *
460
         * @see jp.co.future.uroborosql.SqlAgent#query(java.util.function.Supplier)
461
         */
462
        @Override
463
        public SqlQuery query(final Supplier<String> supplier) {
464
                return this.query(supplier.get());
1✔
465
        }
466

467
        /**
468
         * {@inheritDoc}
469
         *
470
         * @see jp.co.future.uroborosql.SqlAgent#queryWith(java.lang.String)
471
         */
472
        @Override
473
        public SqlQuery queryWith(final String sql) {
474
                if (sql == null || "".equals(sql)) {
1✔
475
                        throw new IllegalArgumentException("sql is required.");
1✔
476
                }
477
                return new SqlQueryImpl(this, context().setSql(sql));
1✔
478
        }
479

480
        /**
481
         * {@inheritDoc}
482
         *
483
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.String)
484
         */
485
        @Override
486
        public SqlUpdate update(final String sqlName) {
487
                if ("".equals(sqlName)) {
1✔
488
                        throw new IllegalArgumentException("sqlName is required.");
1✔
489
                }
490
                return new SqlUpdateImpl(this, context().setSqlName(sqlName));
1✔
491
        }
492

493
        /**
494
         * {@inheritDoc}
495
         *
496
         * @see jp.co.future.uroborosql.SqlAgent#update(java.util.function.Supplier)
497
         */
498
        @Override
499
        public SqlUpdate update(final Supplier<String> supplier) {
500
                return this.update(supplier.get());
1✔
501
        }
502

503
        /**
504
         * {@inheritDoc}
505
         *
506
         * @see jp.co.future.uroborosql.SqlAgent#updateWith(java.lang.String)
507
         */
508
        @Override
509
        public SqlUpdate updateWith(final String sql) {
510
                if (sql == null || "".equals(sql)) {
1✔
511
                        throw new IllegalArgumentException("sql is required.");
1✔
512
                }
513
                return new SqlUpdateImpl(this, context().setSql(sql));
1✔
514
        }
515

516
        /**
517
         * {@inheritDoc}
518
         *
519
         * @see jp.co.future.uroborosql.SqlAgent#batch(java.lang.String)
520
         */
521
        @Override
522
        public SqlBatch batch(final String sqlName) {
523
                if ("".equals(sqlName)) {
1✔
524
                        throw new IllegalArgumentException("sqlName is required.");
1✔
525
                }
526
                return new SqlBatchImpl(this, context().setSqlName(sqlName));
1✔
527
        }
528

529
        /**
530
         * {@inheritDoc}
531
         *
532
         * @see jp.co.future.uroborosql.SqlAgent#batch(java.util.function.Supplier)
533
         */
534
        @Override
535
        public SqlBatch batch(final Supplier<String> supplier) {
536
                return this.batch(supplier.get());
1✔
537
        }
538

539
        /**
540
         * {@inheritDoc}
541
         *
542
         * @see jp.co.future.uroborosql.SqlAgent#batchWith(java.lang.String)
543
         */
544
        @Override
545
        public SqlBatch batchWith(final String sql) {
546
                if (sql == null || "".equals(sql)) {
1✔
547
                        throw new IllegalArgumentException("sql is required.");
1✔
548
                }
549
                return new SqlBatchImpl(this, context().setSql(sql));
1✔
550
        }
551

552
        /**
553
         * {@inheritDoc}
554
         *
555
         * @see jp.co.future.uroborosql.SqlAgent#proc(java.lang.String)
556
         */
557
        @Override
558
        public Procedure proc(final String sqlName) {
559
                if ("".equals(sqlName)) {
1✔
560
                        throw new IllegalArgumentException("sqlName is required.");
1✔
561
                }
562
                return new ProcedureImpl(this, context().setSqlName(sqlName));
1✔
563
        }
564

565
        /**
566
         * {@inheritDoc}
567
         *
568
         * @see jp.co.future.uroborosql.SqlAgent#proc(java.util.function.Supplier)
569
         */
570
        @Override
571
        public Procedure proc(final Supplier<String> supplier) {
572
                return this.proc(supplier.get());
1✔
573
        }
574

575
        /**
576
         * {@inheritDoc}
577
         *
578
         * @see jp.co.future.uroborosql.SqlAgent#procWith(java.lang.String)
579
         */
580
        @Override
581
        public Procedure procWith(final String sql) {
582
                if (sql == null || "".equals(sql)) {
1✔
583
                        throw new IllegalArgumentException("sql is required.");
1✔
584
                }
585
                return new ProcedureImpl(this, context().setSql(sql));
1✔
586
        }
587

588
        /**
589
         * {@inheritDoc}
590
         *
591
         * @see jp.co.future.uroborosql.SqlAgent#query(java.lang.Class)
592
         */
593
        @Override
594
        public <E> SqlEntityQuery<E> query(final Class<? extends E> entityType) {
595
                var handler = this.getEntityHandler();
1✔
596
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
NEW
597
                        throw new IllegalArgumentException("Entity type not supported");
×
598
                }
599
                try {
600
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
601
                        var context = handler.createSelectContext(this, metadata, entityType, false);
1✔
602
                        context.setSqlKind(SqlKind.ENTITY_SELECT);
1✔
603
                        return new SqlEntityQueryImpl<>(this, handler, metadata, context, entityType);
1✔
NEW
604
                } catch (SQLException ex) {
×
NEW
605
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_SELECT, ex);
×
606
                }
607
        }
608

609
        /**
610
         * {@inheritDoc}
611
         *
612
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
613
         */
614
        @Override
615
        public <E> int inserts(final Class<E> entityType,
616
                        final Stream<E> entities,
617
                        final InsertsCondition<? super E> condition,
618
                        final InsertsType insertsType) {
619
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
620
                        return bulkInsert(entityType, entities, condition, null);
1✔
621
                } else {
622
                        return batchInsert(entityType, entities, condition, null);
1✔
623
                }
624
        }
625

626
        /**
627
         * {@inheritDoc}
628
         *
629
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream)
630
         */
631
        @Override
632
        public <E> int inserts(final Class<E> entityType,
633
                        final Stream<E> entities) {
634
                return inserts(entityType, entities, getInsertsCondition(getInsertsType()));
1✔
635
        }
636

637
        /**
638
         * {@inheritDoc}
639
         *
640
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
641
         */
642
        @Override
643
        public <E> int inserts(final Class<E> entityType,
644
                        final Stream<E> entities,
645
                        final InsertsCondition<? super E> condition) {
646
                return inserts(entityType, entities, condition, getInsertsType());
1✔
647
        }
648

649
        /**
650
         * {@inheritDoc}
651
         *
652
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
653
         */
654
        @Override
655
        public <E> int inserts(final Class<E> entityType,
656
                        final Stream<E> entities,
657
                        final InsertsType insertsType) {
658
                return inserts(entityType, entities, getInsertsCondition(insertsType), insertsType);
1✔
659
        }
660

661
        /**
662
         * {@inheritDoc}
663
         *
664
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
665
         */
666
        @Override
667
        public <E> int inserts(final Stream<E> entities,
668
                        final InsertsCondition<? super E> condition,
669
                        final InsertsType insertsType) {
670
                var iterator = entities.iterator();
1✔
671
                if (!iterator.hasNext()) {
1✔
672
                        return 0;
1✔
673
                }
674
                var firstEntity = iterator.next();
1✔
675
                @SuppressWarnings("unchecked")
676
                var type = (Class<E>) firstEntity.getClass();
1✔
677
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
678
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
679
                return inserts(type, stream, condition, insertsType);
1✔
680
        }
681

682
        /**
683
         * {@inheritDoc}
684
         *
685
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream)
686
         */
687
        @Override
688
        public <E> int inserts(final Stream<E> entities) {
689
                return inserts(entities, getInsertsCondition(getInsertsType()));
1✔
690
        }
691

692
        /**
693
         * {@inheritDoc}
694
         *
695
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
696
         */
697
        @Override
698
        public <E> int inserts(final Stream<E> entities,
699
                        final InsertsCondition<? super E> condition) {
700
                return inserts(entities, condition, getInsertsType());
1✔
701
        }
702

703
        /**
704
         * {@inheritDoc}
705
         *
706
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
707
         */
708
        @Override
709
        public <E> int inserts(final Stream<E> entities,
710
                        final InsertsType insertsType) {
711
                return inserts(entities, getInsertsCondition(insertsType), insertsType);
1✔
712
        }
713

714
        /**
715
         * {@inheritDoc}
716
         *
717
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
718
         */
719
        @Override
720
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
721
                        final Stream<E> entities,
722
                        final InsertsCondition<? super E> condition,
723
                        final InsertsType insertsType) {
724
                var insertedEntities = new ArrayList<E>();
1✔
725
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
726
                        bulkInsert(entityType, entities, condition, insertedEntities);
1✔
727
                } else {
728
                        batchInsert(entityType, entities, condition, insertedEntities);
1✔
729
                }
730
                return insertedEntities.stream();
1✔
731
        }
732

733
        /**
734
         * {@inheritDoc}
735
         *
736
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
737
         */
738
        @Override
739
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
740
                        final Stream<E> entities,
741
                        final InsertsCondition<? super E> condition) {
742
                return insertsAndReturn(entityType, entities, condition, getInsertsType());
1✔
743
        }
744

745
        /**
746
         * {@inheritDoc}
747
         *
748
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream)
749
         */
750
        @Override
751
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
752
                        final Stream<E> entities) {
753
                return insertsAndReturn(entityType, entities, getInsertsCondition(getInsertsType()));
1✔
754
        }
755

756
        /**
757
         * {@inheritDoc}
758
         *
759
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
760
         */
761
        @Override
762
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
763
                        final Stream<E> entities,
764
                        final InsertsType insertsType) {
765
                return insertsAndReturn(entityType, entities, getInsertsCondition(insertsType), insertsType);
1✔
766
        }
767

768
        /**
769
         * {@inheritDoc}
770
         *
771
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
772
         */
773
        @Override
774
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
775
                        final InsertsCondition<? super E> condition,
776
                        final InsertsType insertsType) {
777
                var iterator = entities.iterator();
1✔
778
                if (!iterator.hasNext()) {
1✔
779
                        return Stream.empty();
1✔
780
                }
781
                var firstEntity = iterator.next();
1✔
782
                @SuppressWarnings("unchecked")
783
                var type = (Class<E>) firstEntity.getClass();
1✔
784
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
785
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
786
                return insertsAndReturn(type, stream, condition, insertsType);
1✔
787
        }
788

789
        /**
790
         * {@inheritDoc}
791
         *
792
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
793
         */
794
        @Override
795
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
796
                        final InsertsCondition<? super E> condition) {
797
                return insertsAndReturn(entities, condition, getInsertsType());
1✔
798
        }
799

800
        /**
801
         * {@inheritDoc}
802
         *
803
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream)
804
         */
805
        @Override
806
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities) {
807
                return insertsAndReturn(entities, getInsertsCondition(getInsertsType()));
1✔
808
        }
809

810
        /**
811
         * {@inheritDoc}
812
         *
813
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
814
         */
815
        @Override
816
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
817
                        final InsertsType insertsType) {
818
                return insertsAndReturn(entities, getInsertsCondition(insertsType), insertsType);
1✔
819
        }
820

821
        /**
822
         * {@inheritDoc}
823
         *
824
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
825
         */
826
        @Override
827
        public <E> int updates(final Class<E> entityType,
828
                        final Stream<E> entities,
829
                        final UpdatesCondition<? super E> condition) {
830
                return batchUpdate(entityType, entities, condition, null);
1✔
831
        }
832

833
        /**
834
         * {@inheritDoc}
835
         *
836
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
837
         */
838
        @Override
839
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
840
                        final Stream<E> entities,
841
                        final UpdatesCondition<? super E> condition) {
842
                var updatedEntities = new ArrayList<E>();
1✔
843
                batchUpdate(entityType, entities, condition, updatedEntities);
1✔
844
                return updatedEntities.stream();
1✔
845
        }
846

847
        /**
848
         * {@inheritDoc}
849
         *
850
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.lang.Class, java.util.stream.Stream)
851
         */
852
        @Override
853
        public <E> int updates(final Class<E> entityType,
854
                        final Stream<E> entities) {
855
                return updates(entityType, entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
856
        }
857

858
        /**
859
         * {@inheritDoc}
860
         *
861
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream)
862
         */
863
        @Override
864
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
865
                        final Stream<E> entities) {
866
                return updatesAndReturn(entityType, entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
867
        }
868

869
        /**
870
         * {@inheritDoc}
871
         *
872
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
873
         */
874
        @Override
875
        public <E> int updates(final Stream<E> entities,
876
                        final UpdatesCondition<? super E> condition) {
877
                var iterator = entities.iterator();
1✔
878
                if (!iterator.hasNext()) {
1✔
NEW
879
                        return 0;
×
880
                }
881
                var firstEntity = iterator.next();
1✔
882
                @SuppressWarnings("unchecked")
883
                var type = (Class<E>) firstEntity.getClass();
1✔
884
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
885
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
886
                return updates(type, stream, condition);
1✔
887
        }
888

889
        /**
890
         * {@inheritDoc}
891
         *
892
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
893
         */
894
        @Override
895
        public <E> Stream<E> updatesAndReturn(final Stream<E> entities,
896
                        final UpdatesCondition<? super E> condition) {
897
                var iterator = entities.iterator();
1✔
898
                if (!iterator.hasNext()) {
1✔
NEW
899
                        return Stream.empty();
×
900
                }
901
                var firstEntity = iterator.next();
1✔
902
                @SuppressWarnings("unchecked")
903
                var type = (Class<E>) firstEntity.getClass();
1✔
904
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
905
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
906
                return updatesAndReturn(type, stream, condition);
1✔
907
        }
908

909
        /**
910
         * {@inheritDoc}
911
         *
912
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.util.stream.Stream)
913
         */
914
        @Override
915
        public <E> int updates(final Stream<E> entities) {
916
                return updates(entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
917
        }
918

919
        /**
920
         * {@inheritDoc}
921
         *
922
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.util.stream.Stream)
923
         */
924
        @Override
925
        public <E> Stream<E> updatesAndReturn(final Stream<E> entities) {
926
                return updatesAndReturn(entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
927
        }
928

929
        private <E> InsertsCondition<? super E> getInsertsCondition(final InsertsType insertsType) {
930
                return InsertsType.BATCH.equals(insertsType)
1✔
931
                                ? DEFAULT_BATCH_INSERTS_WHEN_CONDITION
1✔
932
                                : DEFAULT_BULK_INSERTS_WHEN_CONDITION;
1✔
933
        }
934

935
        /**
936
         * 複数エンティティのBULK INSERTを実行
937
         *
938
         * @param <E> エンティティの型
939
         * @param entityType エンティティの型
940
         * @param entities エンティティ
941
         * @param condition 一括INSERT用のフレームの判定条件
942
         * @param insertedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
943
         * @return SQL実行結果
944
         */
945
        private <E> int batchInsert(final Class<E> entityType, final Stream<E> entities,
946
                        final InsertsCondition<? super E> condition, final List<E> insertedEntities) {
947
                EntityHandler<E> handler = this.getEntityHandler();
1✔
948
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
949
                        throw new IllegalArgumentException("Entity type not supported");
1✔
950
                }
951

952
                try {
953
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
954
                        var context = handler.createBatchInsertContext(this, metadata, entityType);
1✔
955
                        context.setSqlKind(SqlKind.ENTITY_BATCH_INSERT);
1✔
956

957
                        var count = 0;
1✔
958
                        var entityList = new ArrayList<E>();
1✔
959
                        var isFirst = true;
1✔
960
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
961
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
962
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
963
                                var entity = iterator.next();
1✔
964

965
                                if (!entityType.isInstance(entity)) {
1✔
966
                                        throw new IllegalArgumentException("Entity types do not match");
1✔
967
                                }
968

969
                                if (isFirst) {
1✔
970
                                        isFirst = false;
1✔
971
                                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
972
                                        autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
973

974
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
975
                                        var excludeColumns = autoGeneratedColumns;
1✔
976
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
977
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
978
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
979
                                                                        && col.getValue(entity) != null)
1✔
980
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
981
                                }
982

983
                                entityList.add(entity);
1✔
984
                                if (insertedEntities != null) {
1✔
985
                                        insertedEntities.add(entity);
1✔
986
                                }
987

988
                                handler.setInsertParams(context, entity);
1✔
989
                                context.addBatch();
1✔
990
                                // SQLのID項目IF分岐判定をtrueにするためにaddBatch()の後に保持しておいたID項目をcontextにバインドする
991
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
992
                                        context.paramMap(nonNullObjectIdFlags);
1✔
993
                                }
994
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
995
                                        count += Arrays.stream(doBatchInsert(context, handler, entityList, autoGeneratedColumns)).sum();
1✔
996
                                        entityList.clear();
1✔
997
                                }
998
                        }
1✔
999
                        return count + (context.batchCount() != 0
1✔
1000
                                        ? Arrays.stream(doBatchInsert(context, handler, entityList, autoGeneratedColumns)).sum()
1✔
1001
                                        : 0);
1✔
NEW
1002
                } catch (SQLException ex) {
×
NEW
1003
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_BATCH_INSERT, ex);
×
1004
                }
1005
        }
1006

1007
        private <E> int[] doBatchInsert(final ExecutionContext context, final EntityHandler<E> handler,
1008
                        final List<E> entityList, final List<MappingColumn> autoGeneratedColumns) throws SQLException {
1009
                var counts = handler.doBatchInsert(this, context);
1✔
1010
                if (!autoGeneratedColumns.isEmpty()) {
1✔
1011
                        var ids = context.getGeneratedKeyValues();
1✔
1012
                        var idx = 0;
1✔
1013
                        for (E ent : entityList) {
1✔
1014
                                for (var col : autoGeneratedColumns) {
1✔
1015
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
1016
                                }
1✔
1017
                        }
1✔
1018
                }
1019

1020
                return counts;
1✔
1021
        }
1022

1023
        /**
1024
         * 複数エンティティのINSERTをバッチ実行
1025
         *
1026
         * @param <E> エンティティの型
1027
         * @param entityType エンティティの型
1028
         * @param entities エンティティ
1029
         * @param condition 一括INSERT用のフレームの判定条件
1030
         * @param insertedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
1031
         * @return SQL実行結果
1032
         */
1033
        private <E> int bulkInsert(final Class<E> entityType, final Stream<E> entities,
1034
                        final InsertsCondition<? super E> condition, final List<E> insertedEntities) {
1035
                EntityHandler<E> handler = this.getEntityHandler();
1✔
1036
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
NEW
1037
                        throw new IllegalArgumentException("Entity type not supported");
×
1038
                }
1039

1040
                try {
1041
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1042
                        var context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
1043
                        context.setSqlKind(SqlKind.ENTITY_BULK_INSERT);
1✔
1044

1045
                        var frameCount = 0;
1✔
1046
                        var count = 0;
1✔
1047
                        var isFirst = true;
1✔
1048
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
1049
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
1050
                        var entityList = new ArrayList<E>();
1✔
1051
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1052
                                var entity = iterator.next();
1✔
1053

1054
                                if (!entityType.isInstance(entity)) {
1✔
NEW
1055
                                        throw new IllegalArgumentException("Entity types do not match");
×
1056
                                }
1057

1058
                                if (isFirst) {
1✔
1059
                                        isFirst = false;
1✔
1060
                                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
1061
                                        autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
1062

1063
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
1064
                                        var excludeColumns = autoGeneratedColumns;
1✔
1065
                                        // indexなしのid値がcontextにバインドされないため、値としてkey=trueを退避しておく
1066
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
1067
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
1068
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
1069
                                                                        && col.getValue(entity) != null)
1✔
1070
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
1071
                                }
1072
                                // 退避しておいたid値をこのタイミングで設定する
1073
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
1074
                                        context.paramMap(nonNullObjectIdFlags);
1✔
1075
                                }
1076

1077
                                entityList.add(entity);
1✔
1078
                                if (insertedEntities != null) {
1✔
1079
                                        insertedEntities.add(entity);
1✔
1080
                                }
1081

1082
                                handler.setBulkInsertParams(context, entity, frameCount);
1✔
1083
                                frameCount++;
1✔
1084

1085
                                if (condition.test(context, frameCount, entity)) {
1✔
1086
                                        count += doBulkInsert(context, entityType, handler, metadata, autoGeneratedColumns, entityList);
1✔
1087
                                        frameCount = 0;
1✔
1088
                                        entityList.clear();
1✔
1089

1090
                                        // 新しいExecutionContextを作成する前にgeneratedKeyColumnsを退避しておく
1091
                                        var generatedKeyColumns = context.getGeneratedKeyColumns();
1✔
1092
                                        context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
1093
                                        context.setSqlKind(SqlKind.ENTITY_BULK_INSERT);
1✔
1094
                                        // 実行結果から生成されたIDを取得できるようにPreparedStatementにIDカラムを渡す
1095
                                        context.setGeneratedKeyColumns(generatedKeyColumns);
1✔
1096
                                }
1097
                        }
1✔
1098
                        return count + (frameCount > 0
1✔
1099
                                        ? doBulkInsert(context, entityType, handler, metadata, autoGeneratedColumns, entityList)
1✔
1100
                                        : 0);
1✔
1101

NEW
1102
                } catch (SQLException ex) {
×
NEW
1103
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_BULK_INSERT, ex);
×
1104
                }
1105
        }
1106

1107
        private <E> int doBulkInsert(final ExecutionContext context, final Class<E> entityType,
1108
                        final EntityHandler<E> handler,
1109
                        final TableMetadata metadata, final List<MappingColumn> autoGeneratedColumns, final List<E> entityList)
1110
                        throws SQLException {
1111
                var count = handler.doBulkInsert(this,
1✔
1112
                                handler.setupSqlBulkInsertContext(this, context, metadata, entityType, entityList.size()));
1✔
1113

1114
                if (!autoGeneratedColumns.isEmpty()) {
1✔
1115
                        var ids = context.getGeneratedKeyValues();
1✔
1116
                        var idx = 0;
1✔
1117
                        for (E ent : entityList) {
1✔
1118
                                for (var col : autoGeneratedColumns) {
1✔
1119
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
1120
                                }
1✔
1121
                        }
1✔
1122
                }
1123

1124
                return count;
1✔
1125
        }
1126

1127
        /**
1128
         * 複数エンティティのBULK UPDATEを実行
1129
         *
1130
         * @param <E> エンティティの型
1131
         * @param entityType エンティティの型
1132
         * @param entities エンティティ
1133
         * @param condition 一括更新用のフレームの判定条件
1134
         * @param updatedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
1135
         * @return SQL実行結果
1136
         */
1137
        private <E> int batchUpdate(final Class<E> entityType, final Stream<E> entities,
1138
                        final UpdatesCondition<? super E> condition, final List<E> updatedEntities) {
1139
                var handler = this.getEntityHandler();
1✔
1140
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
NEW
1141
                        throw new IllegalArgumentException("Entity type not supported");
×
1142
                }
1143

1144
                try {
1145
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1146
                        var context = handler.createBatchUpdateContext(this, metadata, entityType);
1✔
1147
                        context.setSqlKind(SqlKind.BATCH_UPDATE);
1✔
1148

1149
                        var versionColumn = MappingUtils.getVersionMappingColumn(metadata.getSchema(),
1✔
1150
                                        entityType);
1151
                        var entityCount = 0;
1✔
1152
                        var updateCount = 0;
1✔
1153
                        var entityList = new ArrayList<E>();
1✔
1154
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1155
                                var entity = iterator.next();
1✔
1156

1157
                                if (!entityType.isInstance(entity)) {
1✔
NEW
1158
                                        throw new IllegalArgumentException("Entity types do not match");
×
1159
                                }
1160

1161
                                entityList.add(entity);
1✔
1162
                                if (updatedEntities != null) {
1✔
1163
                                        updatedEntities.add(entity);
1✔
1164
                                }
1165
                                entityCount++;
1✔
1166

1167
                                handler.setUpdateParams(context, entity);
1✔
1168
                                context.addBatch();
1✔
1169

1170
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
1171
                                        updateCount += Arrays.stream(handler.doBatchUpdate(this, context)).sum();
1✔
1172
                                        entityList.clear();
1✔
1173
                                }
1174
                        }
1✔
1175
                        updateCount = updateCount + (context.batchCount() != 0
1✔
1176
                                        ? Arrays.stream(handler.doBatchUpdate(this, context)).sum()
1✔
1177
                                        : 0);
1✔
1178

1179
                        if (updatedEntities != null && versionColumn.isPresent()) {
1✔
1180
                                var vColumn = versionColumn.get();
1✔
1181
                                var keyColumns = metadata.getColumns().stream()
1✔
1182
                                                .filter(TableMetadata.Column::isKey)
1✔
1183
                                                .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
1184
                                                .map(c -> MappingUtils.getMappingColumnMap(metadata.getSchema(), entityType, SqlKind.NONE)
1✔
1185
                                                                .get(c.getCamelColumnName()))
1✔
1186
                                                .collect(Collectors.toList());
1✔
1187

1188
                                if (keyColumns.size() == 1) {
1✔
1189
                                        // 単一キーの場合はIN句で更新した行を一括取得し@Versionのついたフィールドを更新する
1190
                                        var keyColumn = keyColumns.get(0);
1✔
1191
                                        var updatedEntityMap = updatedEntities.stream()
1✔
1192
                                                        .collect(Collectors.groupingBy(e -> keyColumn.getValue(e)));
1✔
1193

1194
                                        // updatedEntitiesのサイズが大きいとin句の上限にあたるため、1000件ずつに分割して検索する
1195
                                        var keyList = new ArrayList<>(updatedEntityMap.keySet());
1✔
1196
                                        var entitySize = updatedEntities.size();
1✔
1197

1198
                                        for (var start = 0; start < entitySize; start = start + IN_CLAUSE_MAX_PARAM_SIZE) {
1✔
1199
                                                var end = Math.min(start + IN_CLAUSE_MAX_PARAM_SIZE, entitySize);
1✔
1200
                                                var subList = keyList.subList(start, end);
1✔
1201

1202
                                                query(entityType).in(keyColumn.getCamelName(), subList).stream()
1✔
1203
                                                                .map(e -> {
1✔
1204
                                                                        var updatedEntity = updatedEntityMap.get(keyColumn.getValue(e)).get(0);
1✔
1205
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
1✔
1206
                                                                        return updatedEntity;
1✔
1207
                                                                }).count();
1✔
1208
                                        }
1209
                                } else if (keyColumns.size() > 1) {
1✔
1210
                                        // 複合キーの場合はIN句で一括取得できないため1件ずつ取得して@Versionのついたフィールドを更新する
1211
                                        updatedEntities.stream()
1✔
1212
                                                        .map(updatedEntity -> {
1✔
NEW
1213
                                                                var keyValues = keyColumns.stream().map(k -> k.getValue(updatedEntity)).toArray();
×
NEW
1214
                                                                find(entityType, keyValues).ifPresent(e -> {
×
NEW
1215
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
×
NEW
1216
                                                                });
×
NEW
1217
                                                                return updatedEntity;
×
1218
                                                        }).count();
1✔
1219
                                }
1220
                        }
1221
                        if (versionColumn.isPresent() && getDialect().supportsEntityBulkUpdateOptimisticLock()
1✔
1222
                                        && updateCount != entityCount) {
1223
                                // バージョンカラムの指定があり、更新件数と更新対象Entityの件数が不一致の場合は楽観ロックエラーとする
1224
                                throw new OptimisticLockException(String.format(
1✔
1225
                                                "An error occurred due to optimistic locking.%nExecuted SQL [%n%s]%nBatch Entity Count: %d, Update Count: %d.",
1226
                                                context.getExecutableSql(), entityCount, updateCount));
1✔
1227
                        }
1228
                        return updateCount;
1✔
NEW
1229
                } catch (SQLException ex) {
×
NEW
1230
                        throw new EntitySqlRuntimeException(SqlKind.BATCH_UPDATE, ex);
×
1231
                }
1232
        }
1233

1234
        /**
1235
         * {@inheritDoc}
1236
         *
1237
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext)
1238
         */
1239
        @Override
1240
        public ResultSet query(final ExecutionContext executionContext) throws SQLException {
1241
                // パラメータログを出力する
1242
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1243

1244
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1245
                        executionContext.setSqlKind(SqlKind.SELECT);
1✔
1246
                }
1247

1248
                // コンテキスト変換
1249
                transformContext(executionContext);
1✔
1250

1251
                var stmt = getPreparedStatement(executionContext);
1✔
1252

1253
                // INパラメータ設定
1254
                executionContext.bindParams(stmt);
1✔
1255

1256
                // REPLで実行するための文字列をREPLログに出力する
1257
                outputReplLog(executionContext);
1✔
1258

1259
                Instant startTime = null;
1✔
1260
                if (LOG.isDebugEnabled()) {
1✔
NEW
1261
                        LOG.debug("Execute query sql. sqlName: {}", executionContext.getSqlName());
×
1262
                }
1263
                if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1264
                        startTime = Instant.now(getSqlConfig().getClock());
1✔
1265
                }
1266

1267
                try {
1268
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1269
                        var maxRetryCount = getMaxRetryCount();
1✔
1270
                        if (executionContext.getMaxRetryCount() >= 0) {
1✔
1271
                                maxRetryCount = executionContext.getMaxRetryCount();
1✔
1272
                        }
1273

1274
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1275
                        var retryWaitTime = getRetryWaitTime();
1✔
1276
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1277
                                retryWaitTime = executionContext.getRetryWaitTime();
1✔
1278
                        }
1279
                        var loopCount = 0;
1✔
1280
                        var dialect = getDialect();
1✔
1281
                        ResultSet rs = null;
1✔
1282
                        try {
1283
                                do {
1284
                                        try {
1285
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1286
                                                        setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1287
                                                }
1288
                                                rs = stmt.executeQuery();
1✔
1289
                                                // Query実行後イベント発行
1290
                                                if (getSqlConfig().getEventListenerHolder().hasSqlQueryListener()) {
1✔
1291
                                                        var eventObj = new SqlQueryEvent(executionContext, rs, stmt.getConnection(), stmt);
1✔
1292
                                                        for (var listener : getSqlConfig().getEventListenerHolder().getSqlQueryListeners()) {
1✔
1293
                                                                listener.accept(eventObj);
1✔
1294
                                                        }
1✔
1295
                                                        rs = eventObj.getResultSet();
1✔
1296
                                                }
1297
                                                stmt.closeOnCompletion();
1✔
1298
                                                return new InnerResultSet(rs, stmt);
1✔
1299
                                        } catch (SQLException ex) {
1✔
1300
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1301
                                                        rollback(RETRY_SAVEPOINT_NAME);
1✔
1302
                                                }
1303
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1304
                                                var sqlState = ex.getSQLState();
1✔
1305
                                                var pessimisticLockingErrorCodes = dialect.getPessimisticLockingErrorCodes();
1✔
1306
                                                if (maxRetryCount > loopCount
1✔
1307
                                                                && (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState))) {
1✔
1308
                                                        if (LOG.isDebugEnabled()) {
1✔
1309
                                                                LOG.debug(String.format(
×
1310
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1311
                                                                                loopCount + 1, retryWaitTime));
×
1312
                                                        }
1313
                                                        if (retryWaitTime > 0) {
1✔
1314
                                                                try {
1315
                                                                        Thread.sleep(retryWaitTime);
1✔
1316
                                                                } catch (InterruptedException ie) {
×
1317
                                                                        // do nothing
1318
                                                                }
1✔
1319
                                                        }
1320
                                                } else {
1321
                                                        if (pessimisticLockingErrorCodes.contains(errorCode)
1✔
1322
                                                                        || pessimisticLockingErrorCodes.contains(sqlState)) {
1✔
1323
                                                                throw new PessimisticLockException(executionContext, ex);
1✔
1324
                                                        } else {
1325
                                                                throw ex;
1✔
1326
                                                        }
1327
                                                }
1328
                                        } finally {
1329
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1330
                                                        releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1331
                                                }
1332
                                                executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1333
                                        }
1334
                                } while (maxRetryCount > loopCount++);
1✔
1335
                        } catch (SQLException | RuntimeException ex) {
1✔
1336
                                if (rs != null && !rs.isClosed()) {
1✔
1337
                                        rs.close();
1✔
1338
                                }
1339
                                throw ex;
1✔
1340
                        }
×
1341
                        return null;
×
1342
                } catch (SQLException ex) {
1✔
1343
                        handleException(executionContext, ex);
×
1344
                        return null;
×
1345
                } finally {
1346
                        // 後処理
1347
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1348
                                PERFORMANCE_LOG.info("SQL execution time [{}({})] : [{}]",
1✔
1349
                                                generateSqlName(executionContext),
1✔
1350
                                                executionContext.getSqlKind(),
1✔
1351
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1352
                        }
1353
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1354
                }
1355
        }
1356

1357
        /**
1358
         * {@inheritDoc}
1359
         *
1360
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1361
         *      jp.co.future.uroborosql.converter.ResultSetConverter)
1362
         */
1363
        @Override
1364
        public <T> Stream<T> query(final ExecutionContext executionContext, final ResultSetConverter<T> converter)
1365
                        throws SQLException {
1366
                var rs = query(executionContext);
1✔
1367
                return StreamSupport.stream(new ResultSetSpliterator<>(rs, converter), false).onClose(() -> {
1✔
1368
                        try {
1369
                                if (rs != null && !rs.isClosed()) {
1✔
1370
                                        rs.close();
1✔
1371
                                }
1372
                        } catch (SQLException ex) {
×
1373
                                // do nothing
1374
                        }
1✔
1375
                });
1✔
1376
        }
1377

1378
        /**
1379
         * {@inheritDoc}
1380
         *
1381
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1382
         *      jp.co.future.uroborosql.utils.CaseFormat)
1383
         */
1384
        @Override
1385
        public List<Map<String, Object>> query(final ExecutionContext executionContext, final CaseFormat caseFormat)
1386
                        throws SQLException {
1387
                try (var stream = query(executionContext, new MapResultSetConverter(getSqlConfig(), caseFormat))) {
1✔
1388
                        return stream.collect(Collectors.toList());
1✔
1389
                }
1390
        }
1391

1392
        /**
1393
         * @see jp.co.future.uroborosql.SqlAgent#update(jp.co.future.uroborosql.context.ExecutionContext)
1394
         */
1395
        @Override
1396
        public int update(final ExecutionContext executionContext) throws SQLException {
1397
                // パラメータログを出力する
1398
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1399

1400
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1401
                        executionContext.setSqlKind(SqlKind.UPDATE);
1✔
1402
                }
1403

1404
                // コンテキスト変換
1405
                transformContext(executionContext);
1✔
1406

1407
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1408
                if (executionContext.getUpdateDelegate() != null) {
1✔
1409
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1410
                        if (LOG.isInfoEnabled()) {
1✔
1411
                                LOG.info("Performs update delegate of update process.");
1✔
1412
                        }
1413
                        return executionContext.getUpdateDelegate().apply(executionContext);
1✔
1414
                }
1415

1416
                Instant startTime = null;
1✔
1417

1418
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1419

1420
                        // INパラメータ設定
1421
                        executionContext.bindParams(stmt);
1✔
1422

1423
                        // REPLで実行するための文字列をREPLログに出力する
1424
                        outputReplLog(executionContext);
1✔
1425

1426
                        if (LOG.isDebugEnabled()) {
1✔
NEW
1427
                                LOG.debug("Execute update sql. sqlName: {}", executionContext.getSqlName());
×
1428
                        }
1429

1430
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1431
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1432
                        }
1433

1434
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1435
                        var maxRetryCount = getMaxRetryCount();
1✔
1436
                        if (executionContext.getMaxRetryCount() >= 0) {
1✔
1437
                                maxRetryCount = executionContext.getMaxRetryCount();
1✔
1438
                        }
1439

1440
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1441
                        var retryWaitTime = getRetryWaitTime();
1✔
1442
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1443
                                retryWaitTime = executionContext.getRetryWaitTime();
1✔
1444
                        }
1445
                        var loopCount = 0;
1✔
1446
                        do {
1447
                                try {
1448
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1449
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1450
                                        }
1451
                                        var count = stmt.executeUpdate();
1✔
1452
                                        // Update実行後イベント発行
1453
                                        if (getSqlConfig().getEventListenerHolder().hasSqlUpdateListener()) {
1✔
1454
                                                var eventObj = new SqlUpdateEvent(executionContext, count, stmt.getConnection(), stmt);
1✔
1455
                                                for (var listener : getSqlConfig().getEventListenerHolder().getSqlUpdateListeners()) {
1✔
1456
                                                        listener.accept(eventObj);
1✔
1457
                                                }
1✔
1458
                                                count = eventObj.getCount();
1✔
1459
                                        }
1460
                                        if ((SqlKind.INSERT.equals(executionContext.getSqlKind()) ||
1✔
1461
                                                        SqlKind.ENTITY_INSERT.equals(executionContext.getSqlKind()) ||
1✔
1462
                                                        SqlKind.BULK_INSERT.equals(executionContext.getSqlKind()) ||
1✔
1463
                                                        SqlKind.ENTITY_BULK_INSERT.equals(executionContext.getSqlKind()))
1✔
1464
                                                        && executionContext.hasGeneratedKeyColumns()) {
1✔
1465
                                                try (var rs = stmt.getGeneratedKeys()) {
1✔
1466
                                                        var generatedKeyValues = new ArrayList<>();
1✔
1467
                                                        while (rs.next()) {
1✔
1468
                                                                for (var i = 1; i <= executionContext.getGeneratedKeyColumns().length; i++) {
1✔
1469
                                                                        generatedKeyValues.add(rs.getObject(i));
1✔
1470
                                                                }
1471
                                                        }
1472
                                                        executionContext.setGeneratedKeyValues(
1✔
1473
                                                                        generatedKeyValues.toArray(new Object[generatedKeyValues.size()]));
1✔
1474
                                                }
1475
                                        }
1476
                                        return count;
1✔
1477
                                } catch (SQLException ex) {
1✔
1478
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1479
                                                rollback(RETRY_SAVEPOINT_NAME);
1✔
1480
                                        }
1481
                                        if (maxRetryCount > loopCount) {
1✔
1482
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1483
                                                var sqlState = ex.getSQLState();
1✔
1484
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1485
                                                        if (LOG.isDebugEnabled()) {
1✔
1486
                                                                LOG.debug(String.format(
×
1487
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1488
                                                                                loopCount + 1, retryWaitTime));
×
1489
                                                        }
1490
                                                        if (retryWaitTime > 0) {
1✔
1491
                                                                try {
1492
                                                                        Thread.sleep(retryWaitTime);
1✔
1493
                                                                } catch (InterruptedException ie) {
×
1494
                                                                        // do nothing
1495
                                                                }
1✔
1496
                                                        }
1497
                                                } else {
1498
                                                        throw ex;
1✔
1499
                                                }
1500
                                        } else {
1✔
1501
                                                throw ex;
1✔
1502
                                        }
1503
                                } finally {
1504
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1505
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1506
                                        }
1507
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1508
                                }
1509
                        } while (maxRetryCount > loopCount++);
1✔
1510
                        return 0;
×
1511
                } catch (SQLException ex) {
1✔
1512
                        handleException(executionContext, ex);
×
1513
                        return 0;
×
1514
                } finally {
1515
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1516
                                PERFORMANCE_LOG.info("SQL execution time [{}({})] : [{}]",
1✔
1517
                                                generateSqlName(executionContext),
1✔
1518
                                                executionContext.getSqlKind(),
1✔
1519
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1520
                        }
1521
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1522
                }
1523
        }
1524

1525
        /**
1526
         * @see jp.co.future.uroborosql.SqlAgent#batch(jp.co.future.uroborosql.context.ExecutionContext)
1527
         */
1528
        @Override
1529
        public int[] batch(final ExecutionContext executionContext) throws SQLException {
1530
                // バッチ処理の場合大量のログが出力されるため、パラメータログの出力を抑止する
1531
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.TRUE.toString());
1✔
1532

1533
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1534
                        executionContext.setSqlKind(SqlKind.BATCH_INSERT);
1✔
1535
                }
1536

1537
                // コンテキスト変換
1538
                transformContext(executionContext);
1✔
1539

1540
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1541
                if (executionContext.getUpdateDelegate() != null) {
1✔
1542
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1543
                        if (LOG.isInfoEnabled()) {
1✔
1544
                                LOG.info("Performs update delegate of batch process.");
1✔
1545
                        }
1546
                        return new int[] { executionContext.getUpdateDelegate().apply(executionContext) };
1✔
1547
                }
1548

1549
                Instant startTime = null;
1✔
1550

1551
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1552

1553
                        // INパラメータ設定
1554
                        executionContext.bindBatchParams(stmt);
1✔
1555

1556
                        if (LOG.isDebugEnabled()) {
1✔
NEW
1557
                                LOG.debug("Execute batch sql. sqlName: {}", executionContext.getSqlName());
×
1558
                        }
1559
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1560
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1561
                        }
1562

1563
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1564
                        var maxRetryCount = getMaxRetryCount();
1✔
1565
                        if (executionContext.getMaxRetryCount() >= 0) {
1✔
1566
                                maxRetryCount = executionContext.getMaxRetryCount();
1✔
1567
                        }
1568

1569
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1570
                        var retryWaitTime = getRetryWaitTime();
1✔
1571
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1572
                                retryWaitTime = executionContext.getRetryWaitTime();
×
1573
                        }
1574
                        var loopCount = 0;
1✔
1575
                        do {
1576
                                try {
1577
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1578
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
×
1579
                                        }
1580
                                        var counts = stmt.executeBatch();
1✔
1581
                                        // Batch実行後イベント発行
1582
                                        if (getSqlConfig().getEventListenerHolder().hasSqlBatchListener()) {
1✔
1583
                                                var eventObj = new SqlBatchEvent(executionContext, counts, stmt.getConnection(), stmt);
1✔
1584
                                                for (var listener : getSqlConfig().getEventListenerHolder().getSqlBatchListeners()) {
1✔
1585
                                                        listener.accept(eventObj);
1✔
1586
                                                }
1✔
1587
                                                counts = eventObj.getCounts();
1✔
1588
                                        }
1589
                                        if ((SqlKind.BATCH_INSERT.equals(executionContext.getSqlKind()) ||
1✔
1590
                                                        SqlKind.ENTITY_BATCH_INSERT.equals(executionContext.getSqlKind()))
1✔
1591
                                                        && executionContext.hasGeneratedKeyColumns()) {
1✔
1592
                                                try (var rs = stmt.getGeneratedKeys()) {
1✔
1593
                                                        var generatedKeyValues = new ArrayList<>();
1✔
1594
                                                        while (rs.next()) {
1✔
1595
                                                                for (var i = 1; i <= executionContext.getGeneratedKeyColumns().length; i++) {
1✔
1596
                                                                        generatedKeyValues.add(rs.getObject(i));
1✔
1597
                                                                }
1598
                                                        }
1599
                                                        executionContext.setGeneratedKeyValues(
1✔
1600
                                                                        generatedKeyValues.toArray(new Object[generatedKeyValues.size()]));
1✔
1601
                                                }
1602
                                        }
1603
                                        return counts;
1✔
1604
                                } catch (SQLException ex) {
1✔
1605
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1606
                                                rollback(RETRY_SAVEPOINT_NAME);
×
1607
                                        }
1608
                                        if (maxRetryCount > loopCount) {
1✔
1609
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1610
                                                var sqlState = ex.getSQLState();
1✔
1611
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1612
                                                        if (LOG.isDebugEnabled()) {
1✔
1613
                                                                LOG.debug(String.format(
×
1614
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1615
                                                                                loopCount + 1, retryWaitTime));
×
1616
                                                        }
1617
                                                        if (retryWaitTime > 0) {
1✔
1618
                                                                try {
1619
                                                                        Thread.sleep(retryWaitTime);
1✔
1620
                                                                } catch (InterruptedException ie) {
×
1621
                                                                        // do nothing
1622
                                                                }
1✔
1623
                                                        }
1624
                                                } else {
1625
                                                        throw ex;
×
1626
                                                }
1627
                                        } else {
1✔
1628
                                                throw ex;
1✔
1629
                                        }
1630
                                } finally {
1631
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1632
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
×
1633
                                        }
1634
                                        executionContext.clearBatch();
1✔
1635
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1636
                                }
1637
                        } while (maxRetryCount > loopCount++);
1✔
1638
                        return null;
×
1639
                } catch (SQLException ex) {
1✔
1640
                        handleException(executionContext, ex);
×
1641
                        return null;
×
1642
                } finally {
1643
                        // 後処理
1644
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1645
                                PERFORMANCE_LOG.info("SQL execution time [{}({})] : [{}]",
1✔
1646
                                                generateSqlName(executionContext),
1✔
1647
                                                executionContext.getSqlKind(),
1✔
1648
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1649
                        }
1650
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1651
                }
1652
        }
1653

1654
        /**
1655
         * {@inheritDoc}
1656
         *
1657
         * @see jp.co.future.uroborosql.SqlAgent#procedure(jp.co.future.uroborosql.context.ExecutionContext)
1658
         */
1659
        @Override
1660
        public Map<String, Object> procedure(final ExecutionContext executionContext) throws SQLException {
1661
                // パラメータログを出力する
1662
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1663

1664
                // procedureやfunctionの場合、SQL文法エラーになるためバインドパラメータコメントを出力しない
1665
                executionContext.contextAttrs().put(CTX_ATTR_KEY_OUTPUT_BIND_COMMENT, false);
1✔
1666

1667
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1668
                        executionContext.setSqlKind(SqlKind.PROCEDURE);
1✔
1669
                }
1670

1671
                // コンテキスト変換
1672
                transformContext(executionContext);
1✔
1673

1674
                Instant startTime = null;
1✔
1675

1676
                try (var callableStatement = getCallableStatement(executionContext)) {
1✔
1677

1678
                        // パラメータ設定
1679
                        executionContext.bindParams(callableStatement);
1✔
1680

1681
                        if (LOG.isDebugEnabled()) {
1✔
NEW
1682
                                LOG.debug("Execute stored procedure. sqlName: {}", executionContext.getSqlName());
×
1683
                        }
1684
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1685
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1686
                        }
1687

1688
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1689
                        var maxRetryCount = getMaxRetryCount();
1✔
1690
                        if (executionContext.getMaxRetryCount() >= 0) {
1✔
1691
                                maxRetryCount = executionContext.getMaxRetryCount();
1✔
1692
                        }
1693

1694
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1695
                        var retryWaitTime = getRetryWaitTime();
1✔
1696
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1697
                                retryWaitTime = executionContext.getRetryWaitTime();
1✔
1698
                        }
1699
                        var loopCount = 0;
1✔
1700
                        do {
1701
                                try {
1702
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1703
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1704
                                        }
1705
                                        var result = callableStatement.execute();
1✔
1706
                                        // Procedure実行後イベント発行
1707
                                        if (getSqlConfig().getEventListenerHolder().hasProcedureListener()) {
1✔
1708
                                                var eventObj = new ProcedureEvent(executionContext, result, callableStatement.getConnection(),
1✔
1709
                                                                callableStatement);
1710
                                                for (var listener : getSqlConfig().getEventListenerHolder().getProcedureListeners()) {
1✔
1711
                                                        listener.accept(eventObj);
1✔
1712
                                                }
1✔
1713
                                                result = eventObj.isResult();
1✔
1714
                                        }
1715
                                        break;
1716
                                } catch (SQLException ex) {
1✔
1717
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1718
                                                rollback(RETRY_SAVEPOINT_NAME);
1✔
1719
                                        }
1720
                                        if (maxRetryCount > loopCount) {
1✔
1721
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1722
                                                var sqlState = ex.getSQLState();
1✔
1723
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1724
                                                        if (LOG.isDebugEnabled()) {
1✔
1725
                                                                LOG.debug(String.format(
×
1726
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1727
                                                                                loopCount + 1, retryWaitTime));
×
1728
                                                        }
1729
                                                        if (retryWaitTime > 0) {
1✔
1730
                                                                try {
1731
                                                                        Thread.sleep(retryWaitTime);
1✔
1732
                                                                } catch (InterruptedException ie) {
×
1733
                                                                        // do nothing
1734
                                                                }
1✔
1735
                                                        }
1736
                                                } else {
1737
                                                        throw ex;
1✔
1738
                                                }
1739
                                        } else {
1✔
1740
                                                throw ex;
1✔
1741
                                        }
1742
                                } finally {
1743
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1744
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1745
                                        }
1746
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1747
                                }
1748
                        } while (maxRetryCount > loopCount++);
1✔
1749
                        // 結果取得
1750
                        return executionContext.getOutParams(callableStatement);
1✔
1751
                } catch (SQLException ex) {
1✔
1752
                        handleException(executionContext, ex);
×
1753
                } finally {
1754
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1755
                                PERFORMANCE_LOG.info("Stored procedure execution time [{}({})] : [{}]",
1✔
1756
                                                generateSqlName(executionContext),
1✔
1757
                                                executionContext.getSqlKind(),
1✔
1758
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1759
                        }
1760
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1761
                }
1762
                return null;
×
1763
        }
1764

1765
        /**
1766
         * ExecutionContextの設定内容を元にSQLを構築する
1767
         *
1768
         * @param executionContext ExecutionContext
1769
         */
1770
        private void transformContext(final ExecutionContext executionContext) {
1771
                var originalSql = executionContext.getSql();
1✔
1772
                var sqlName = executionContext.getSqlName();
1✔
1773
                if (ObjectUtils.isEmpty(originalSql) && getSqlResourceManager() != null) {
1✔
1774
                        originalSql = getSqlResourceManager().getSql(sqlName);
1✔
1775
                        if (ObjectUtils.isEmpty(originalSql)) {
1✔
NEW
1776
                                throw new UroborosqlRuntimeException("sql file:[" + sqlName + "] is not found.");
×
1777
                        }
1778
                }
1779

1780
                // SQL-IDの付与
1781
                if (originalSql.contains(keySqlId)) {
1✔
1782
                        var sqlId = executionContext.getSqlId();
1✔
1783
                        if (ObjectUtils.isEmpty(sqlId)) {
1✔
1784
                                sqlId = sqlName;
1✔
1785
                        }
1786
                        if (ObjectUtils.isEmpty(sqlId)) {
1✔
NEW
1787
                                sqlId = String.valueOf(originalSql.hashCode());
×
1788
                        }
1789

1790
                        originalSql = originalSql.replace(keySqlId, sqlId);
1✔
1791
                }
1792

1793
                if (SQL_LOG.isInfoEnabled() && sqlName != null) {
1✔
1794
                        if (executionContext.getSqlKind().isEntityType()) {
1✔
1795
                                SQL_LOG.info("EntityClass : {}", sqlName);
1✔
1796
                        } else if (getSqlResourceManager().existSql(sqlName)) {
1✔
1797
                                SQL_LOG.info("SQLPath : {}", getSqlResourceManager().getSqlPath(sqlName));
1✔
1798
                        }
1799
                }
1800

1801
                // SQL変換前イベント発行
1802
                if (getSqlConfig().getEventListenerHolder().hasTransformSqlListener()) {
1✔
1803
                        var eventObj = new TransformSqlEvent(executionContext, originalSql);
1✔
1804
                        getSqlConfig().getEventListenerHolder().getTransformSqlListeners()
1✔
1805
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1806
                        originalSql = eventObj.getSql();
1✔
1807
                }
1808
                executionContext.setSql(originalSql);
1✔
1809

1810
                // Dialectに合わせたエスケープキャラクタの設定
1811
                executionContext.param(Dialect.PARAM_KEY_ESCAPE_CHAR, getDialect().getEscapeChar());
1✔
1812

1813
                // SQLパース前イベントの呼出
1814
                if (executionContext.batchCount() == 0
1✔
1815
                                && getSqlConfig().getEventListenerHolder().hasBeforeParseSqlListener()) {
1✔
1816
                        var eventObj = new BeforeParseSqlEvent(executionContext);
1✔
1817
                        getSqlConfig().getEventListenerHolder().getBeforeParseSqlListeners()
1✔
1818
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1819
                }
1820

1821
                if (SQL_LOG.isDebugEnabled()) {
1✔
NEW
1822
                        SQL_LOG.debug("Template SQL[{}{}{}]", System.lineSeparator(), originalSql, System.lineSeparator());
×
1823
                }
1824

1825
                if (ObjectUtils.isEmpty(executionContext.getExecutableSql())) {
1✔
1826
                        // SQLパーサーによるパース処理
1827
                        var outputBindComment = (boolean) executionContext.contextAttrs().getOrDefault(
1✔
1828
                                        CTX_ATTR_KEY_OUTPUT_BIND_COMMENT, true);
1✔
1829
                        var sqlParser = new SqlParserImpl(originalSql, sqlConfig.getExpressionParser(),
1✔
1830
                                        getDialect().isRemoveTerminator(), outputBindComment);
1✔
1831
                        var contextTransformer = sqlParser.parse();
1✔
1832
                        contextTransformer.transform(executionContext);
1✔
1833

1834
                        if (COVERAGE_HANDLER_REF.get() != null) {
1✔
1835
                                // SQLカバレッジ用のログを出力する
1836
                                var coverageData = new CoverageData(sqlName, originalSql,
1✔
1837
                                                contextTransformer.getPassedRoute());
1✔
1838
                                if (COVERAGE_LOG.isDebugEnabled()) {
1✔
NEW
1839
                                        COVERAGE_LOG.debug("coverage data: {}", coverageData);
×
1840
                                }
1841

1842
                                COVERAGE_HANDLER_REF.get().accept(coverageData);
1✔
1843
                        }
1844
                }
1845

1846
                if (SQL_LOG.isInfoEnabled()) {
1✔
1847
                        SQL_LOG.info("Executed SQL[{}{}{}]", System.lineSeparator(), executionContext.getExecutableSql(),
1✔
1848
                                        System.lineSeparator());
1✔
1849
                }
1850
        }
1✔
1851

1852
        /** 時間計測用のログに出力するSQL名を生成する.
1853
         *
1854
         * @param executionContext ExecutionContext
1855
         * @return SQL名. SQL名が取得できない場合はSQL_ID、または空文字を返却する
1856
         */
1857
        private String generateSqlName(final ExecutionContext executionContext) {
1858
                if (executionContext.getSqlName() != null) {
1✔
1859
                        return executionContext.getSqlName();
1✔
1860
                } else {
1861
                        return Objects.toString(executionContext.getSqlId(), "");
1✔
1862
                }
1863
        }
1864

1865
        /**
1866
         * 例外発生時ハンドラー
1867
         *
1868
         * @param executionContext ExecutionContext
1869
         * @param ex SQL例外
1870
         * @throws SQLException SQL例外
1871
         */
1872
        private void handleException(final ExecutionContext executionContext, final SQLException ex) throws SQLException {
1873
                var cause = ex;
1✔
1874

1875
                while (cause.getNextException() != null) {
1✔
1876
                        cause = cause.getNextException();
1✔
1877
                }
1878

1879
                if (outputExceptionLog && LOG.isErrorEnabled()) {
1✔
1880
                        var builder = new StringBuilder();
1✔
1881
                        builder.append(System.lineSeparator()).append("Exception occurred in SQL execution.")
1✔
1882
                                        .append(System.lineSeparator());
1✔
1883
                        builder.append("Executed SQL[").append(executionContext.getExecutableSql()).append("]")
1✔
1884
                                        .append(System.lineSeparator());
1✔
1885
                        if (executionContext instanceof ExecutionContextImpl) {
1✔
1886
                                var bindParameters = ((ExecutionContextImpl) executionContext).getBindParameters();
1✔
1887
                                for (var i = 0; i < bindParameters.length; i++) {
1✔
1888
                                        var parameter = bindParameters[i];
1✔
1889
                                        builder.append("Bind Parameter.[INDEX[").append(i + 1).append("], ").append(parameter.toString())
1✔
1890
                                                        .append("]").append(System.lineSeparator());
1✔
1891
                                }
1892
                        }
1893
                        LOG.error(builder.toString(), cause);
1✔
1894
                }
1895

1896
                throw cause;
1✔
1897
        }
1898

1899
        /**
1900
         * REPLでSQLを実行するためのコマンドをログとしてREPL_LOGに出力する.
1901
         *
1902
         * @param executionContext executionContext
1903
         */
1904
        private void outputReplLog(final ExecutionContext executionContext) {
1905
                if (!(REPL_LOG.isInfoEnabled() &&
1✔
1906
                                executionContext.getSqlName() != null &&
1✔
1907
                                (SqlKind.SELECT.equals(executionContext.getSqlKind()) ||
1✔
1908
                                                SqlKind.UPDATE.equals(executionContext.getSqlKind())))) {
1✔
1909
                        // REPLログ出力対象でない場合は何もしない
1910
                        return;
1✔
1911
                }
1912

1913
                var builder = new StringBuilder();
1✔
1914

1915
                if (SqlKind.SELECT.equals(executionContext.getSqlKind())) {
1✔
1916
                        builder.append("query ");
1✔
1917
                } else {
1918
                        builder.append("update ");
1✔
1919
                }
1920

1921
                builder.append(executionContext.getSqlName());
1✔
1922

1923
                var params = new ArrayList<Parameter>();
1✔
1924
                for (var bindName : executionContext.getBindNames()) {
1✔
1925
                        params.add(executionContext.getParam(bindName));
1✔
1926
                }
1✔
1927
                if (!params.isEmpty()) {
1✔
1928
                        builder.append(" ");
1✔
1929
                        builder.append(SqlParamUtils.formatPrams(params));
1✔
1930
                }
1931
                REPL_LOG.info("REPL command: {}", builder.toString());
1✔
1932
        }
1✔
1933

1934
        /**
1935
         *
1936
         * {@inheritDoc}
1937
         *
1938
         * @see jp.co.future.uroborosql.SqlAgent#find(java.lang.Class, java.lang.Object[])
1939
         */
1940
        @SuppressWarnings("unchecked")
1941
        @Override
1942
        public <E> Optional<E> find(final Class<? extends E> entityType, final Object... keys) {
1943
                @SuppressWarnings("rawtypes")
1944
                EntityHandler handler = this.getEntityHandler();
1✔
1945
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1946
                        throw new IllegalArgumentException("Entity type not supported");
×
1947
                }
1948

1949
                try {
1950
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1951
                        var keyNames = metadata.getColumns().stream()
1✔
1952
                                        .filter(TableMetadata.Column::isKey)
1✔
1953
                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
1954
                                        .map(TableMetadata.Column::getColumnName)
1✔
1955
                                        .map(CaseFormat.CAMEL_CASE::convert)
1✔
1956
                                        .toArray(String[]::new);
1✔
1957

1958
                        if (keyNames.length != keys.length) {
1✔
1959
                                throw new IllegalArgumentException("Number of keys does not match");
×
1960
                        }
1961
                        var params = new HashMap<String, Object>();
1✔
1962
                        for (var i = 0; i < keys.length; i++) {
1✔
1963
                                params.put(keyNames[i], keys[i]);
1✔
1964
                        }
1965

1966
                        var context = handler.createSelectContext(this, metadata, entityType, true);
1✔
1967
                        context.setSqlKind(SqlKind.ENTITY_SELECT).paramMap(params);
1✔
1968

1969
                        try (var stream = handler.doSelect(this, context, entityType)) {
1✔
1970
                                return stream.findFirst();
1✔
1971
                        }
NEW
1972
                } catch (SQLException ex) {
×
NEW
1973
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_SELECT, ex);
×
1974
                }
1975
        }
1976

1977
        /**
1978
         * {@inheritDoc}
1979
         *
1980
         * @see jp.co.future.uroborosql.SqlAgent#insert(java.lang.Object)
1981
         */
1982
        @SuppressWarnings("unchecked")
1983
        @Override
1984
        public <E> int insert(final E entity) {
1985
                if (entity instanceof Stream) {
1✔
1986
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
1987
                }
1988

1989
                @SuppressWarnings("rawtypes")
1990
                EntityHandler handler = this.getEntityHandler();
1✔
1991
                if (!handler.getEntityType().isInstance(entity)) {
1✔
1992
                        throw new IllegalArgumentException("Entity type not supported");
×
1993
                }
1994

1995
                try {
1996
                        var entityType = entity.getClass();
1✔
1997
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1998
                        var context = handler.createInsertContext(this, metadata, entityType);
1✔
1999
                        context.setSqlKind(SqlKind.ENTITY_INSERT);
1✔
2000

2001
                        // 自動採番カラムの取得とcontextへの設定を行う
2002
                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
2003
                        var autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
2004

2005
                        handler.setInsertParams(context, entity);
1✔
2006
                        var count = handler.doInsert(this, context, entity);
1✔
2007

2008
                        if (!autoGeneratedColumns.isEmpty()) {
1✔
2009
                                var ids = context.getGeneratedKeyValues();
1✔
2010
                                var idx = 0;
1✔
2011
                                for (var col : autoGeneratedColumns) {
1✔
2012
                                        setEntityIdValue(entity, ids[idx++], col);
1✔
2013
                                }
1✔
2014
                        }
2015

2016
                        return count;
1✔
2017
                } catch (SQLException ex) {
1✔
2018
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_INSERT, ex);
1✔
2019
                }
2020
        }
2021

2022
        /**
2023
         * {@inheritDoc}
2024
         *
2025
         * @see jp.co.future.uroborosql.SqlAgent#insertAndReturn(java.lang.Object)
2026
         */
2027
        @Override
2028
        public <E> E insertAndReturn(final E entity) {
2029
                insert(entity);
1✔
2030
                return entity;
1✔
2031
        }
2032

2033
        /**
2034
         * {@inheritDoc}
2035
         *
2036
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.Object)
2037
         */
2038
        @SuppressWarnings("unchecked")
2039
        @Override
2040
        public <E> int update(final E entity) {
2041
                if (entity instanceof Stream) {
1✔
2042
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2043
                }
2044

2045
                @SuppressWarnings("rawtypes")
2046
                EntityHandler handler = this.getEntityHandler();
1✔
2047
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2048
                        throw new IllegalArgumentException("Entity type not supported");
×
2049
                }
2050

2051
                try {
2052
                        var type = entity.getClass();
1✔
2053
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
2054
                        var context = handler.createUpdateContext(this, metadata, type, true);
1✔
2055
                        context.setSqlKind(SqlKind.ENTITY_UPDATE);
1✔
2056
                        handler.setUpdateParams(context, entity);
1✔
2057
                        var count = handler.doUpdate(this, context, entity);
1✔
2058

2059
                        MappingUtils.getVersionMappingColumn(metadata.getSchema(), type).ifPresent(versionColumn -> {
1✔
2060
                                if (count == 0) {
1✔
2061
                                        throw new OptimisticLockException(context);
1✔
2062
                                } else {
2063
                                        var columnMap = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.NONE);
1✔
2064
                                        var keys = metadata.getColumns().stream()
1✔
2065
                                                        .filter(TableMetadata.Column::isKey)
1✔
2066
                                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
2067
                                                        .map(c -> columnMap.get(c.getCamelColumnName()).getValue(entity))
1✔
2068
                                                        .toArray();
1✔
2069

2070
                                        find(type, keys).ifPresent(e -> versionColumn.setValue(entity, versionColumn.getValue(e)));
1✔
2071
                                }
2072
                        });
1✔
2073
                        return count;
1✔
NEW
2074
                } catch (SQLException ex) {
×
NEW
2075
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_UPDATE, ex);
×
2076
                }
2077
        }
2078

2079
        /**
2080
         * {@inheritDoc}
2081
         *
2082
         * @see jp.co.future.uroborosql.SqlAgent#updateAndReturn(java.lang.Object)
2083
         */
2084
        @Override
2085
        public <E> E updateAndReturn(final E entity) {
2086
                update(entity);
1✔
2087
                return entity;
1✔
2088
        }
2089

2090
        /**
2091
         * {@inheritDoc}
2092
         *
2093
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class)
2094
         */
2095
        @Override
2096
        public <E> SqlEntityUpdate<E> update(final Class<? extends E> entityType) {
2097
                var handler = this.getEntityHandler();
1✔
2098
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2099
                        throw new IllegalArgumentException("Entity type not supported");
×
2100
                }
2101

2102
                try {
2103
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2104

2105
                        var context = handler.createUpdateContext(this, metadata, entityType, false);
1✔
2106
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2107

2108
                        return new SqlEntityUpdateImpl<>(this, handler, metadata, context);
1✔
NEW
2109
                } catch (SQLException ex) {
×
NEW
2110
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2111
                }
2112
        }
2113

2114
        /**
2115
         * {@inheritDoc}
2116
         *
2117
         * @see jp.co.future.uroborosql.SqlAgent#merge(java.lang.Object)
2118
         */
2119
        @Override
2120
        public <E> int merge(final E entity) {
2121
                mergeAndReturn(entity);
1✔
2122
                return 1;
1✔
2123
        }
2124

2125
        /**
2126
         * {@inheritDoc}
2127
         *
2128
         * @see jp.co.future.uroborosql.SqlAgent#mergeAndReturn(java.lang.Object)
2129
         */
2130
        @Override
2131
        public <E> E mergeAndReturn(final E entity) {
2132
                return doMergeAndReturn(entity, false);
1✔
2133
        }
2134

2135
        /**
2136
         * {@inheritDoc}
2137
         *
2138
         * @see jp.co.future.uroborosql.SqlAgent#mergeWithLocking(java.lang.Object)
2139
         */
2140
        @Override
2141
        public <E> int mergeWithLocking(final E entity) {
2142
                mergeWithLockingAndReturn(entity);
1✔
2143
                return 1;
1✔
2144
        }
2145

2146
        /**
2147
         * {@inheritDoc}
2148
         *
2149
         * @see jp.co.future.uroborosql.SqlAgent#mergeWithLockingAndReturn(java.lang.Object)
2150
         */
2151
        @Override
2152
        public <E> E mergeWithLockingAndReturn(final E entity) {
2153
                return doMergeAndReturn(entity, true);
1✔
2154
        }
2155

2156
        /**
2157
         * エンティティのMERGE(INSERTまたはUPDATE)を実行し、MERGEしたエンティティを返却する.<br>
2158
         * locking引数が<code>true</code>の場合、マージの最初に発行するEntityの検索で悲観ロック(forUpdateNoWait)を行う
2159
         *
2160
         * @param <E> エンティティ型
2161
         * @param entity エンティティ
2162
         * @param locking エンティティの悲観ロックを行うかどうか
2163
         * @return MERGEしたエンティティ
2164
         *
2165
         */
2166
        @SuppressWarnings("unchecked")
2167
        private <E> E doMergeAndReturn(final E entity, final boolean locking) {
2168
                if (entity instanceof Stream) {
1✔
2169
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2170
                }
2171

2172
                var handler = this.getEntityHandler();
1✔
2173
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2174
                        throw new IllegalArgumentException("Entity type not supported.");
×
2175
                }
2176

2177
                var type = entity.getClass();
1✔
2178
                try {
2179
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
2180
                        var keyColumns = metadata.getKeyColumns();
1✔
2181

2182
                        if (keyColumns.isEmpty()) {
1✔
2183
                                throw new IllegalArgumentException("Entity has no keys.");
×
2184
                        }
2185

2186
                        var mappingColumns = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.UPDATE);
1✔
2187
                        var query = (SqlEntityQuery<E>) query(type);
1✔
2188
                        for (var column : keyColumns) {
1✔
2189
                                var camelName = column.getCamelColumnName();
1✔
2190
                                query.equal(camelName, mappingColumns.get(camelName).getValue(entity));
1✔
2191
                        }
1✔
2192
                        if (locking) {
1✔
2193
                                query.forUpdateNoWait();
1✔
2194
                        }
2195
                        return query.first()
1✔
2196
                                        .map(findEntity -> {
1✔
2197
                                                for (var mappingColumn : mappingColumns.values()) {
1✔
2198
                                                        if (!mappingColumn.isId()) {
1✔
2199
                                                                var value = mappingColumn.getValue(entity);
1✔
2200
                                                                if (value != null) {
1✔
2201
                                                                        mappingColumn.setValue(findEntity, value);
1✔
2202
                                                                }
2203
                                                        }
2204
                                                }
1✔
2205
                                                return updateAndReturn(findEntity);
1✔
2206
                                        }).orElseGet(() -> insertAndReturn(entity));
1✔
NEW
2207
                } catch (SQLException ex) {
×
NEW
2208
                        throw new EntitySqlRuntimeException(SqlKind.MERGE, ex);
×
2209
                }
2210
        }
2211

2212
        /**
2213
         * {@inheritDoc}
2214
         *
2215
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Object)
2216
         */
2217
        @Override
2218
        public <E> int delete(final E entity) {
2219
                if (entity instanceof Stream) {
1✔
2220
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2221
                }
2222

2223
                var handler = this.getEntityHandler();
1✔
2224
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2225
                        throw new IllegalArgumentException("Entity type not supported");
×
2226
                }
2227

2228
                try {
2229
                        var type = entity.getClass();
1✔
2230
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
2231
                        var context = handler.createDeleteContext(this, metadata, type, true);
1✔
2232
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2233
                        handler.setDeleteParams(context, entity);
1✔
2234
                        return handler.doDelete(this, context, entity);
1✔
NEW
2235
                } catch (SQLException ex) {
×
NEW
2236
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2237
                }
2238
        }
2239

2240
        /**
2241
         * {@inheritDoc}
2242
         *
2243
         * @see jp.co.future.uroborosql.SqlAgent#deleteAndReturn(java.lang.Object)
2244
         */
2245
        @Override
2246
        public <E> E deleteAndReturn(final E entity) {
2247
                delete(entity);
1✔
2248
                return entity;
1✔
2249
        }
2250

2251
        /**
2252
         * {@inheritDoc}
2253
         *
2254
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class, java.lang.Object[])
2255
         */
2256
        @Override
2257
        public <E> int delete(final Class<? extends E> entityType, final Object... keys) {
2258
                var handler = this.getEntityHandler();
1✔
2259
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2260
                        throw new IllegalArgumentException("Entity type not supported");
×
2261
                }
2262

2263
                try {
2264
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2265
                        TableMetadata.Column keyColumn = null;
1✔
2266
                        var keyColumns = metadata.getKeyColumns();
1✔
2267
                        if (keyColumns.size() == 1) {
1✔
2268
                                keyColumn = keyColumns.get(0);
1✔
2269
                        } else if (keyColumns.isEmpty()) {
1✔
2270
                                keyColumn = metadata.getColumns().get(0);
1✔
2271
                        } else {
2272
                                throw new IllegalArgumentException("Entity has multiple keys");
1✔
2273
                        }
2274
                        return delete(entityType).in(keyColumn.getCamelColumnName(), keys).count();
1✔
NEW
2275
                } catch (SQLException ex) {
×
NEW
2276
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2277
                }
2278
        }
2279

2280
        /**
2281
         * {@inheritDoc}
2282
         *
2283
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class)
2284
         */
2285
        @Override
2286
        public <E> SqlEntityDelete<E> delete(final Class<? extends E> entityType) {
2287
                var handler = this.getEntityHandler();
1✔
2288
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2289
                        throw new IllegalArgumentException("Entity type not supported");
×
2290
                }
2291

2292
                try {
2293
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2294
                        var context = handler.createDeleteContext(this, metadata, entityType, false);
1✔
2295
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2296
                        return new SqlEntityDeleteImpl<>(this, handler, metadata, context);
1✔
NEW
2297
                } catch (SQLException ex) {
×
NEW
2298
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2299
                }
2300
        }
2301

2302
        /**
2303
         * {@inheritDoc}
2304
         *
2305
         * @see jp.co.future.uroborosql.SqlAgent#truncate(java.lang.Class)
2306
         */
2307
        @Override
2308
        public <E> SqlAgent truncate(final Class<? extends E> entityType) {
2309
                var handler = this.getEntityHandler();
1✔
2310
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2311
                        throw new IllegalArgumentException("Entity type not supported");
×
2312
                }
2313
                try {
2314
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2315
                        var context = this.context().setSql("truncate table " + metadata.getTableIdentifier());
1✔
2316
                        context.setSqlKind(SqlKind.TRUNCATE);
1✔
2317
                        update(context);
1✔
2318
                        return this;
1✔
2319
                } catch (SQLException ex) {
1✔
2320
                        throw new EntitySqlRuntimeException(SqlKind.TRUNCATE, ex);
1✔
2321
                }
2322
        }
2323

2324
        /**
2325
         * 自動採番カラムを取得する. 合わせて、{@link ExecutionContext#setGeneratedKeyColumns(String[])} に自動採番カラムを設定する.
2326
         * <pre>
2327
         * 自動採番カラムの条件
2328
         * 1. {@link MappingColumn#isId()} == true 、もしくは{@link TableMetadata.Column#isAutoincrement()} == true であること
2329
         * 2. 1.に加え、エンティティの対象フィールド型がprimitive型、もしくはフィールドの値が<code>null</code>であること
2330
         * </pre>
2331
         *
2332
         * @param <E> entity
2333
         * @param context ExecutionContext
2334
         * @param mappingColumns EntityのMappingColumn配列
2335
         * @param metadata TableMetadata
2336
         * @param entity Entity
2337
         * @return 自動採番カラム配列
2338
         */
2339
        private <E> List<MappingColumn> getAutoGeneratedColumns(final ExecutionContext context,
2340
                        final MappingColumn[] mappingColumns,
2341
                        final TableMetadata metadata,
2342
                        final E entity) {
2343
                var autoGeneratedColumns = new ArrayList<MappingColumn>();
1✔
2344
                var autoGeneratedColumnNames = new ArrayList<String>();
1✔
2345

2346
                if (mappingColumns != null && mappingColumns.length > 0) {
1✔
2347
                        for (var column : metadata.getColumns()) {
1✔
2348
                                if (column.isAutoincrement()) {
1✔
2349
                                        for (var mappingColumn : mappingColumns) {
1✔
2350
                                                if (mappingColumn.getCamelName().equals(column.getCamelColumnName())) {
1✔
2351
                                                        // primitive型か、エンティティの対象フィールドがnullとなっている場合自動生成カラムとする
2352
                                                        if (mappingColumn.getJavaType().getRawType().isPrimitive()
1✔
2353
                                                                        || mappingColumn.getValue(entity) == null) {
1✔
2354
                                                                autoGeneratedColumns.add(mappingColumn);
1✔
2355
                                                                autoGeneratedColumnNames.add(column.getColumnName());
1✔
2356
                                                        }
2357
                                                        break;
2358
                                                }
2359
                                        }
2360
                                }
2361
                        }
1✔
2362
                        for (var mappingColumn : mappingColumns) {
1✔
2363
                                if (!mappingColumn.isId() || autoGeneratedColumns.contains(mappingColumn)) {
1✔
2364
                                        continue;
1✔
2365
                                }
2366
                                for (var column : metadata.getColumns()) {
1✔
2367
                                        if (mappingColumn.getCamelName().equals(column.getCamelColumnName())) {
1✔
2368
                                                // primitive型か、エンティティの対象フィールドがnullとなっている場合自動生成カラムとする
2369
                                                if (mappingColumn.getJavaType().getRawType().isPrimitive()
1✔
2370
                                                                || mappingColumn.getValue(entity) == null) {
1✔
2371
                                                        autoGeneratedColumns.add(mappingColumn);
1✔
2372
                                                        autoGeneratedColumnNames.add(column.getColumnName());
1✔
2373
                                                }
2374
                                                break;
2375
                                        }
2376
                                }
1✔
2377
                        }
2378
                }
2379

2380
                if (!autoGeneratedColumnNames.isEmpty()) {
1✔
2381
                        context.setGeneratedKeyColumns(
1✔
2382
                                        autoGeneratedColumnNames.toArray(new String[autoGeneratedColumnNames.size()]));
1✔
2383
                }
2384
                return autoGeneratedColumns;
1✔
2385
        }
2386

2387
        /**
2388
         * entityにID値を設定する
2389
         * @param entity Entity
2390
         * @param id ID値
2391
         * @param column 設定する対象のカラム
2392
         */
2393
        private void setEntityIdValue(final Object entity, final Object id, final MappingColumn column) {
2394
                var rawType = column.getJavaType().getRawType();
1✔
2395
                try {
2396
                        if (int.class.equals(rawType) || Integer.class.equals(rawType)) {
1✔
2397
                                if (id instanceof Number) {
1✔
2398
                                        column.setValue(entity, ((Number) id).intValue());
1✔
2399
                                } else {
2400
                                        throw new IllegalArgumentException();
1✔
2401
                                }
2402
                        } else if (long.class.equals(rawType) || Long.class.equals(rawType)) {
1✔
2403
                                if (id instanceof Number) {
1✔
2404
                                        column.setValue(entity, ((Number) id).longValue());
1✔
2405
                                } else {
2406
                                        throw new IllegalArgumentException();
1✔
2407
                                }
2408
                        } else if (BigInteger.class.equals(rawType)) {
1✔
2409
                                if (id instanceof BigInteger) {
1✔
NEW
2410
                                        column.setValue(entity, id);
×
2411
                                } else if (id instanceof BigDecimal) {
1✔
2412
                                        column.setValue(entity, ((BigDecimal) id).toBigInteger());
1✔
2413
                                } else if (id instanceof Number) {
1✔
2414
                                        column.setValue(entity, new BigInteger(((Number) id).toString()));
1✔
2415
                                } else {
2416
                                        throw new IllegalArgumentException();
1✔
2417
                                }
2418
                        } else if (BigDecimal.class.equals(rawType)) {
1✔
2419
                                if (id instanceof BigDecimal) {
1✔
2420
                                        column.setValue(entity, id);
1✔
2421
                                } else if (id instanceof BigInteger) {
1✔
NEW
2422
                                        column.setValue(entity, new BigDecimal((BigInteger) id));
×
2423
                                } else if (id instanceof Number) {
1✔
2424
                                        column.setValue(entity, new BigDecimal(((Number) id).toString()));
1✔
2425
                                } else {
2426
                                        throw new IllegalArgumentException();
1✔
2427
                                }
2428
                        } else if (String.class.equals(rawType)) {
1✔
2429
                                if (id instanceof String) {
1✔
NEW
2430
                                        column.setValue(entity, id);
×
2431
                                } else if (id instanceof BigDecimal) {
1✔
2432
                                        column.setValue(entity, ((BigDecimal) id).toPlainString());
1✔
2433
                                } else if (id instanceof Number) {
1✔
2434
                                        column.setValue(entity, ((Number) id).toString());
1✔
2435
                                } else {
2436
                                        column.setValue(entity, id.toString());
1✔
2437
                                }
2438
                        } else {
2439
                                try {
2440
                                        column.setValue(entity, id);
1✔
2441
                                } catch (UroborosqlRuntimeException ex) {
1✔
2442
                                        throw new UroborosqlRuntimeException(
1✔
2443
                                                        "Column is not correct as ID type. column=" + column.getCamelName() + ", type=" + rawType);
1✔
2444
                                }
1✔
2445
                        }
2446
                } catch (IllegalArgumentException ex) {
1✔
2447
                        throw new UroborosqlRuntimeException(
1✔
2448
                                        "Column type and ID type do not match. column=" + column.getCamelName() + ", type="
1✔
2449
                                                        + rawType
2450
                                                        + ", id=" + id + ", type=" + id.getClass().getSimpleName());
1✔
2451
                }
1✔
2452
        }
1✔
2453

2454
        /**
2455
         *
2456
         * {@inheritDoc}
2457
         *
2458
         * @see jp.co.future.uroborosql.SqlAgent#getSqlConfig()
2459
         */
2460
        @Override
2461
        public SqlConfig getSqlConfig() {
2462
                return this.sqlConfig;
1✔
2463
        }
2464

2465
        /**
2466
         *
2467
         * {@inheritDoc}
2468
         *
2469
         * @see jp.co.future.uroborosql.SqlAgent#context()
2470
         */
2471
        @Override
2472
        public ExecutionContext context() {
2473
                return sqlConfig.context();
1✔
2474
        }
2475

2476
        /**
2477
         * {@inheritDoc}
2478
         *
2479
         * @see jp.co.future.uroborosql.SqlAgent#getFetchSize()
2480
         */
2481
        @Override
2482
        public int getFetchSize() {
2483
                return fetchSize;
1✔
2484
        }
2485

2486
        /**
2487
         * {@inheritDoc}
2488
         *
2489
         * @see jp.co.future.uroborosql.SqlAgent#setFetchSize(int)
2490
         */
2491
        @Override
2492
        public SqlAgent setFetchSize(final int fetchSize) {
2493
                this.fetchSize = fetchSize;
1✔
2494
                return this;
1✔
2495
        }
2496

2497
        /**
2498
         * {@inheritDoc}
2499
         *
2500
         * @see jp.co.future.uroborosql.SqlAgent#getQueryTimeout()
2501
         */
2502
        @Override
2503
        public int getQueryTimeout() {
2504
                return queryTimeout;
1✔
2505
        }
2506

2507
        /**
2508
         * {@inheritDoc}
2509
         *
2510
         * @see jp.co.future.uroborosql.SqlAgent#setQueryTimeout(int)
2511
         */
2512
        @Override
2513
        public SqlAgent setQueryTimeout(final int queryTimeout) {
2514
                this.queryTimeout = queryTimeout;
1✔
2515
                return this;
1✔
2516
        }
2517

2518
        /**
2519
         * SQL実行をリトライするSQLエラーコードのリスト を取得します
2520
         *
2521
         * @return SQL実行をリトライするSQLエラーコードのリスト
2522
         */
2523
        public List<String> getSqlRetryCodes() {
2524
                return sqlRetryCodes;
1✔
2525
        }
2526

2527
        /**
2528
         * SQL実行をリトライするSQLエラーコードのリスト を設定します
2529
         *
2530
         * @param sqlRetryCodes SQL実行をリトライするSQLエラーコードのリスト
2531
         * @return SqlAgent
2532
         */
2533
        public SqlAgent setSqlRetryCodes(final List<String> sqlRetryCodes) {
2534
                this.sqlRetryCodes = sqlRetryCodes;
1✔
2535
                return this;
1✔
2536
        }
2537

2538
        /**
2539
         * 最大リトライ回数 を取得します
2540
         *
2541
         * @return 最大リトライ回数
2542
         */
2543
        public int getMaxRetryCount() {
2544
                return maxRetryCount;
1✔
2545
        }
2546

2547
        /**
2548
         * 最大リトライ回数 を設定します
2549
         *
2550
         * @param maxRetryCount 最大リトライ回数
2551
         * @return SqlAgent
2552
         */
2553
        public SqlAgent setMaxRetryCount(final int maxRetryCount) {
2554
                this.maxRetryCount = maxRetryCount;
1✔
2555
                return this;
1✔
2556
        }
2557

2558
        /**
2559
         * リトライタイムアウト時間(ms) を取得します
2560
         *
2561
         * @return リトライタイムアウト時間(ms)
2562
         */
2563
        public int getRetryWaitTime() {
2564
                return retryWaitTime;
1✔
2565
        }
2566

2567
        /**
2568
         * リトライタイムアウト時間(ms) を設定します
2569
         *
2570
         * @param retryWaitTime リトライタイムアウト時間(ms)
2571
         * @return SqlAgent
2572
         */
2573
        public SqlAgent setRetryWaitTime(final int retryWaitTime) {
2574
                this.retryWaitTime = retryWaitTime;
1✔
2575
                return this;
1✔
2576
        }
2577

2578
        /**
2579
         *
2580
         * {@inheritDoc}
2581
         *
2582
         * @see jp.co.future.uroborosql.SqlAgent#getMapKeyCaseFormat()
2583
         */
2584
        @Override
2585
        public CaseFormat getMapKeyCaseFormat() {
2586
                return mapKeyCaseFormat;
1✔
2587
        }
2588

2589
        /**
2590
         *
2591
         * {@inheritDoc}
2592
         *
2593
         * @see jp.co.future.uroborosql.SqlAgent#setMapKeyCaseFormat(jp.co.future.uroborosql.utils.CaseFormat)
2594
         */
2595
        @Override
2596
        public SqlAgent setMapKeyCaseFormat(final CaseFormat mapKeyCaseFormat) {
2597
                this.mapKeyCaseFormat = mapKeyCaseFormat;
1✔
2598
                return this;
1✔
2599
        }
2600

2601
        /**
2602
         *
2603
         * {@inheritDoc}
2604
         *
2605
         * @see jp.co.future.uroborosql.SqlAgent#getInsertsType()
2606
         */
2607
        @Override
2608
        public InsertsType getInsertsType() {
2609
                return this.insertsType;
1✔
2610
        }
2611

2612
        /**
2613
         *
2614
         * {@inheritDoc}
2615
         *
2616
         * @see jp.co.future.uroborosql.SqlAgent#setInsertsType(jp.co.future.uroborosql.enums.InsertsType)
2617
         */
2618
        @Override
2619
        public SqlAgent setInsertsType(final InsertsType defaultInsertsType) {
2620
                this.insertsType = defaultInsertsType;
1✔
2621
                return this;
1✔
2622
        }
2623

2624
        /**
2625
         * SQLリソース管理クラスを取得します。
2626
         *
2627
         * @return SQLリソース管理クラス
2628
         */
2629
        private SqlResourceManager getSqlResourceManager() {
2630
                return sqlConfig.getSqlResourceManager();
1✔
2631
        }
2632

2633
        /**
2634
         * ORM処理クラス を取得します。
2635
         *
2636
         * @return ORM処理クラス
2637
         */
2638
        @SuppressWarnings("unchecked")
2639
        private <E> EntityHandler<E> getEntityHandler() {
2640
                return (EntityHandler<E>) sqlConfig.getEntityHandler();
1✔
2641
        }
2642

2643
        /**
2644
         * Dialect を取得します。
2645
         *
2646
         * @return Dialect
2647
         */
2648
        private Dialect getDialect() {
2649
                return sqlConfig.getDialect();
1✔
2650
        }
2651

2652
        /**
2653
         * ステートメント初期化。
2654
         *
2655
         * @param executionContext ExecutionContext
2656
         * @return PreparedStatement
2657
         * @throws SQLException SQL例外
2658
         */
2659
        private PreparedStatement getPreparedStatement(final ExecutionContext executionContext) throws SQLException {
2660
                var stmt = ((LocalTransactionManager) transactionManager).getPreparedStatement(executionContext);
1✔
2661
                // プロパティ設定
2662
                applyProperties(stmt);
1✔
2663
                return stmt;
1✔
2664
        }
2665

2666
        /**
2667
         * Callableステートメント初期化
2668
         *
2669
         * @param executionContext ExecutionContext
2670
         * @return CallableStatement
2671
         * @throws SQLException SQL例外
2672
         */
2673
        private CallableStatement getCallableStatement(final ExecutionContext executionContext) throws SQLException {
2674
                var stmt = ((LocalTransactionManager) transactionManager).getCallableStatement(executionContext);
1✔
2675
                // プロパティ設定
2676
                applyProperties(stmt);
1✔
2677
                return stmt;
1✔
2678
        }
2679

2680
        /**
2681
         * フェッチサイズとクエリタイムアウトをPreparedStatementに設定する
2682
         *
2683
         * @param preparedStatement PreparedStatement
2684
         * @throws SQLException SQL例外
2685
         */
2686
        private void applyProperties(final PreparedStatement preparedStatement) throws SQLException {
2687
                // フェッチサイズ指定
2688
                if (getFetchSize() >= 0 && !(preparedStatement instanceof CallableStatement)) {
1✔
2689
                        preparedStatement.setFetchSize(getFetchSize());
1✔
2690
                }
2691

2692
                // クエリタイムアウト指定
2693
                if (getQueryTimeout() >= 0) {
1✔
2694
                        preparedStatement.setQueryTimeout(getQueryTimeout());
1✔
2695
                }
2696
        }
1✔
2697

2698
        /**
2699
         * 経過時間を計算し、HH:mm:ss.SSSSSSにフォーマットする.
2700
         *
2701
         * @param start 開始時間
2702
         * @param end 終了時間
2703
         * @return フォーマットした経過時間
2704
         */
2705
        private static String formatElapsedTime(final Instant start, final Instant end) {
2706
                return ELAPSED_TIME_FORMAT.format(LocalTime.MIDNIGHT.plus(Duration.between(start, end)));
1✔
2707
        }
2708

2709
        /**
2710
         * ResultSetをStreamで扱うためのSpliterator
2711
         *
2712
         * @author H.Sugimoto
2713
         *
2714
         * @param <T> ResultSetの1行を変換した型
2715
         */
2716
        private static final class ResultSetSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
2717
                private final ResultSetConverter<T> converter;
2718
                private final ResultSet rs;
2719
                private boolean finished = false;
1✔
2720

2721
                private ResultSetSpliterator(final ResultSet rs, final ResultSetConverter<T> converter) {
2722
                        super(Long.MAX_VALUE, Spliterator.ORDERED);
1✔
2723
                        this.rs = rs;
1✔
2724
                        this.converter = converter;
1✔
2725
                }
1✔
2726

2727
                @Override
2728
                public boolean tryAdvance(final Consumer<? super T> action) {
2729
                        try {
2730
                                if (finished || !rs.next()) {
1✔
2731
                                        if (!rs.isClosed()) {
1✔
2732
                                                rs.close();
1✔
2733
                                        }
2734
                                        finished = true;
1✔
2735
                                        return false;
1✔
2736
                                }
2737
                                action.accept(converter.createRecord(rs));
1✔
2738
                                return true;
1✔
2739
                        } catch (RuntimeException | Error ex) {
1✔
2740
                                try {
2741
                                        if (rs != null && !rs.isClosed()) {
1✔
2742
                                                rs.close();
1✔
2743
                                        }
NEW
2744
                                } catch (SQLException ex2) {
×
NEW
2745
                                        LOG.error(ex2.getMessage(), ex2);
×
2746
                                }
1✔
2747
                                throw ex;
1✔
2748
                        } catch (SQLException ex) {
×
2749
                                try {
2750
                                        if (rs != null && !rs.isClosed()) {
×
2751
                                                rs.close();
×
2752
                                        }
NEW
2753
                                } catch (SQLException ex2) {
×
NEW
2754
                                        LOG.error(ex2.getMessage(), ex2);
×
2755
                                }
×
2756
                                throw new UroborosqlSQLException(ex);
×
2757
                        }
2758
                }
2759
        }
2760

2761
        /**
2762
         * ResultSetのラッパークラス。ResultSetのクローズに合わせてStatementもクローズする。
2763
         *
2764
         * @author H.Sugimoto
2765
         * @version 0.5.0
2766
         */
2767
        private static class InnerResultSet extends AbstractResultSetWrapper {
2768
                /** 同期してクローズするStatement */
2769
                private final Statement stmt;
2770

2771
                /**
2772
                 * コンストラクタ
2773
                 *
2774
                 * @param wrapped 元となるResultSet
2775
                 * @param stmt Statement
2776
                 */
2777
                InnerResultSet(final ResultSet wrapped, final Statement stmt) {
2778
                        super(wrapped);
1✔
2779
                        this.stmt = stmt;
1✔
2780
                }
1✔
2781

2782
                /**
2783
                 * {@inheritDoc}
2784
                 *
2785
                 * @see jp.co.future.uroborosql.AbstractResultSetWrapper#close()
2786
                 */
2787
                @Override
2788
                public void close() throws SQLException {
2789
                        try {
2790
                                super.close();
1✔
2791
                        } finally {
2792
                                try {
2793
                                        if (stmt != null && !stmt.isClosed()) {
1✔
2794
                                                stmt.close();
1✔
2795
                                        }
NEW
2796
                                } catch (SQLException ex) {
×
2797
                                        // do nothing
2798
                                }
1✔
2799
                        }
2800
                }
1✔
2801
        }
2802
}
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