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

future-architect / uroborosql / #713

pending completion
#713

push

HidekiSugimoto189
cleanup test code

7878 of 8886 relevant lines covered (88.66%)

0.89 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

119
        /** カバレッジハンドラ */
120
        private static final AtomicReference<CoverageHandler> coverageHandlerRef = new AtomicReference<>();
1✔
121

122
        /** SQL設定管理クラス */
123
        private final SqlConfig sqlConfig;
124

125
        /** トランザクション管理機能 */
126
        private final TransactionManager transactionManager;
127

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

131
        /** クエリータイムアウト制限値 */
132
        private int queryTimeout = -1;
1✔
133

134
        /** フェッチサイズ */
135
        private int fetchSize = -1;
1✔
136

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

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

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

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

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

152
        /** {@link InsertsType} */
153
        private InsertsType insertsType = InsertsType.BATCH;
1✔
154

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

170
                CoverageHandler handler = null;
1✔
171
                if (sqlCoverageClassName != null) {
1✔
172
                        try {
173
                                handler = (CoverageHandler) Class.forName(sqlCoverageClassName, true,
1✔
174
                                                Thread.currentThread().getContextClassLoader()).getConstructor().newInstance();
1✔
175
                                COVERAGE_LOG.info("CoverageHandler : {}", sqlCoverageClassName);
1✔
176
                        } catch (Exception ex) {
×
177
                                COVERAGE_LOG.warn("Failed to generate CoverageHandler class. Class:{}, Cause:{}", sqlCoverageClassName,
×
178
                                                ex.getMessage());
×
179
                        }
1✔
180
                }
181

182
                if (handler != null) {
1✔
183
                        coverageHandlerRef.set(handler);
1✔
184
                }
185
        }
1✔
186

187
        /**
188
         * コンストラクタ。
189
         *
190
         * @param sqlConfig SQL設定管理クラス
191
         * @param settings 設定情報
192
         * @param connectionContext DB接続情報
193
         */
194
        SqlAgentImpl(final SqlConfig sqlConfig, final Map<String, String> settings,
195
                        final ConnectionContext connectionContext) {
1✔
196
                this.sqlConfig = sqlConfig;
1✔
197
                this.transactionManager = new LocalTransactionManager(sqlConfig, connectionContext);
1✔
198

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

238
        /**
239
         * {@inheritDoc}
240
         *
241
         * @see jp.co.future.uroborosql.SqlAgent#close()
242
         */
243
        @Override
244
        public void close() {
245
                transactionManager.close();
1✔
246
                if (coverageHandlerRef.get() != null) {
1✔
247
                        coverageHandlerRef.get().onSqlAgentClose();
1✔
248
                }
249
        }
1✔
250

251
        /**
252
         *
253
         * {@inheritDoc}
254
         *
255
         * @see jp.co.future.uroborosql.connection.ConnectionManager#getConnection()
256
         */
257
        @Override
258
        public Connection getConnection() {
259
                return transactionManager.getConnection();
1✔
260
        }
261

262
        /**
263
         *
264
         * {@inheritDoc}
265
         *
266
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.lang.Runnable)
267
         */
268
        @Override
269
        public void required(final Runnable runnable) {
270
                transactionManager.required(runnable);
1✔
271
        }
1✔
272

273
        /**
274
         *
275
         * {@inheritDoc}
276
         *
277
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.util.function.Supplier)
278
         */
279
        @Override
280
        public <R> R required(final Supplier<R> supplier) {
281
                return transactionManager.required(supplier);
1✔
282
        }
283

284
        /**
285
         * {@inheritDoc}
286
         *
287
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(jp.co.future.uroborosql.tx.Runnable)
288
         */
289
        @Override
290
        public void requiresNew(final Runnable runnable) {
291
                transactionManager.requiresNew(runnable);
1✔
292
        }
1✔
293

294
        /**
295
         *
296
         * {@inheritDoc}
297
         *
298
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(java.util.function.Supplier)
299
         */
300
        @Override
301
        public <R> R requiresNew(final Supplier<R> supplier) {
302
                return transactionManager.requiresNew(supplier);
1✔
303
        }
304

305
        /**
306
         *
307
         * {@inheritDoc}
308
         *
309
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.lang.Runnable)
310
         */
311
        @Override
312
        public void notSupported(final Runnable runnable) {
313
                transactionManager.notSupported(runnable);
1✔
314
        }
1✔
315

316
        /**
317
         *
318
         * {@inheritDoc}
319
         *
320
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.util.function.Supplier)
321
         */
322
        @Override
323
        public <R> R notSupported(final Supplier<R> supplier) {
324
                return transactionManager.notSupported(supplier);
1✔
325
        }
326

327
        /**
328
         *
329
         * {@inheritDoc}
330
         *
331
         * @see jp.co.future.uroborosql.tx.TransactionManager#setRollbackOnly()
332
         */
333
        @Override
334
        public void setRollbackOnly() {
335
                transactionManager.setRollbackOnly();
1✔
336
        }
1✔
337

338
        /**
339
         *
340
         * {@inheritDoc}
341
         *
342
         * @see jp.co.future.uroborosql.tx.TransactionManager#setSavepoint(java.lang.String)
343
         */
344
        @Override
345
        public void setSavepoint(final String savepointName) {
346
                transactionManager.setSavepoint(savepointName);
1✔
347
        }
1✔
348

349
        /**
350
         *
351
         * {@inheritDoc}
352
         *
353
         * @see jp.co.future.uroborosql.tx.TransactionManager#releaseSavepoint(java.lang.String)
354
         */
355
        @Override
356
        public void releaseSavepoint(final String savepointName) {
357
                transactionManager.releaseSavepoint(savepointName);
1✔
358
        }
1✔
359

360
        /**
361
         *
362
         * {@inheritDoc}
363
         *
364
         * @see jp.co.future.uroborosql.tx.TransactionManager#rollback(java.lang.String)
365
         */
366
        @Override
367
        public void rollback(final String savepointName) {
368
                transactionManager.rollback(savepointName);
1✔
369
        }
1✔
370

371
        /**
372
         *
373
         * {@inheritDoc}
374
         *
375
         * @see jp.co.future.uroborosql.tx.TransactionManager#savepointScope(java.util.function.Supplier)
376
         */
377
        @Override
378
        public <R> R savepointScope(final Supplier<R> supplier) {
379
                return transactionManager.savepointScope(supplier);
1✔
380
        }
381

382
        /**
383
         *
384
         * {@inheritDoc}
385
         *
386
         * @see jp.co.future.uroborosql.tx.TransactionManager#savepointScope(java.lang.Runnable)
387
         */
388
        @Override
389
        public void savepointScope(final Runnable runnable) {
390
                transactionManager.savepointScope(runnable);
1✔
391
        }
1✔
392

393
        /**
394
         *
395
         * {@inheritDoc}
396
         *
397
         * @see jp.co.future.uroborosql.tx.TransactionManager#autoCommitScope(java.util.function.Supplier)
398
         */
399
        @Override
400
        public <R> R autoCommitScope(final Supplier<R> supplier) {
401
                return transactionManager.autoCommitScope(supplier);
1✔
402
        }
403

404
        /**
405
         *
406
         * {@inheritDoc}
407
         *
408
         * @see jp.co.future.uroborosql.tx.TransactionManager#autoCommitScope(java.lang.Runnable)
409
         */
410
        @Override
411
        public void autoCommitScope(final Runnable runnable) {
412
                transactionManager.autoCommitScope(runnable);
1✔
413
        }
1✔
414

415
        /**
416
         *
417
         * {@inheritDoc}
418
         *
419
         * @see jp.co.future.uroborosql.connection.ConnectionManager#commit()
420
         */
421
        @Override
422
        public void commit() {
423
                transactionManager.commit();
1✔
424
        }
1✔
425

426
        /**
427
         *
428
         * {@inheritDoc}
429
         *
430
         * @see jp.co.future.uroborosql.connection.ConnectionManager#rollback()
431
         */
432
        @Override
433
        public void rollback() {
434
                transactionManager.rollback();
1✔
435
        }
1✔
436

437
        /**
438
         * {@inheritDoc}
439
         *
440
         * @see jp.co.future.uroborosql.SqlAgent#query(java.lang.String)
441
         */
442
        @Override
443
        public SqlQuery query(final String sqlName) {
444
                return new SqlQueryImpl(this, context().setSqlName(sqlName));
1✔
445
        }
446

447
        /**
448
         * {@inheritDoc}
449
         *
450
         * @see jp.co.future.uroborosql.SqlAgent#queryWith(java.lang.String)
451
         */
452
        @Override
453
        public SqlQuery queryWith(final String sql) {
454
                return new SqlQueryImpl(this, context().setSql(sql));
1✔
455
        }
456

457
        /**
458
         * {@inheritDoc}
459
         *
460
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.String)
461
         */
462
        @Override
463
        public SqlUpdate update(final String sqlName) {
464
                return new SqlUpdateImpl(this, context().setSqlName(sqlName));
1✔
465
        }
466

467
        /**
468
         * {@inheritDoc}
469
         *
470
         * @see jp.co.future.uroborosql.SqlAgent#updateWith(java.lang.String)
471
         */
472
        @Override
473
        public SqlUpdate updateWith(final String sql) {
474
                return new SqlUpdateImpl(this, context().setSql(sql));
1✔
475
        }
476

477
        /**
478
         * {@inheritDoc}
479
         *
480
         * @see jp.co.future.uroborosql.SqlAgent#batch(java.lang.String)
481
         */
482
        @Override
483
        public SqlBatch batch(final String sqlName) {
484
                return new SqlBatchImpl(this, context().setSqlName(sqlName));
1✔
485
        }
486

487
        /**
488
         * {@inheritDoc}
489
         *
490
         * @see jp.co.future.uroborosql.SqlAgent#batchWith(java.lang.String)
491
         */
492
        @Override
493
        public SqlBatch batchWith(final String sql) {
494
                return new SqlBatchImpl(this, context().setSql(sql));
1✔
495
        }
496

497
        /**
498
         * {@inheritDoc}
499
         *
500
         * @see jp.co.future.uroborosql.SqlAgent#proc(java.lang.String)
501
         */
502
        @Override
503
        public Procedure proc(final String sqlName) {
504
                return new ProcedureImpl(this, context().setSqlName(sqlName));
1✔
505
        }
506

507
        /**
508
         * {@inheritDoc}
509
         *
510
         * @see jp.co.future.uroborosql.SqlAgent#procWith(java.lang.String)
511
         */
512
        @Override
513
        public Procedure procWith(final String sql) {
514
                return new ProcedureImpl(this, context().setSql(sql));
1✔
515
        }
516

517
        /**
518
         * {@inheritDoc}
519
         *
520
         * @see jp.co.future.uroborosql.SqlAgent#query(java.lang.Class)
521
         */
522
        @Override
523
        public <E> SqlEntityQuery<E> query(final Class<? extends E> entityType) {
524
                var handler = this.getEntityHandler();
1✔
525
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
526
                        throw new IllegalArgumentException("Entity type not supported");
×
527
                }
528
                try {
529
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
530
                        var context = handler.createSelectContext(this, metadata, entityType, false);
1✔
531
                        return new SqlEntityQueryImpl<>(this, handler, metadata, context, entityType);
1✔
532
                } catch (SQLException e) {
×
533
                        throw new EntitySqlRuntimeException(SqlKind.SELECT, e);
×
534
                }
535
        }
536

537
        /**
538
         * {@inheritDoc}
539
         *
540
         * @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)
541
         */
542
        @Override
543
        public <E> int inserts(final Class<E> entityType,
544
                        final Stream<E> entities,
545
                        final InsertsCondition<? super E> condition,
546
                        final InsertsType insertsType) {
547
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
548
                        return bulkInsert(entityType, entities, condition, null);
1✔
549
                } else {
550
                        return batchInsert(entityType, entities, condition, null);
1✔
551
                }
552
        }
553

554
        /**
555
         * {@inheritDoc}
556
         *
557
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream)
558
         */
559
        @Override
560
        public <E> int inserts(final Class<E> entityType,
561
                        final Stream<E> entities) {
562
                return inserts(entityType, entities, getInsertsCondition(getInsertsType()));
1✔
563
        }
564

565
        /**
566
         * {@inheritDoc}
567
         *
568
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
569
         */
570
        @Override
571
        public <E> int inserts(final Class<E> entityType,
572
                        final Stream<E> entities,
573
                        final InsertsCondition<? super E> condition) {
574
                return inserts(entityType, entities, condition, getInsertsType());
1✔
575
        }
576

577
        /**
578
         * {@inheritDoc}
579
         *
580
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
581
         */
582
        @Override
583
        public <E> int inserts(final Class<E> entityType,
584
                        final Stream<E> entities,
585
                        final InsertsType insertsType) {
586
                return inserts(entityType, entities, getInsertsCondition(insertsType), insertsType);
1✔
587
        }
588

589
        /**
590
         * {@inheritDoc}
591
         *
592
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
593
         */
594
        @Override
595
        public <E> int inserts(final Stream<E> entities,
596
                        final InsertsCondition<? super E> condition,
597
                        final InsertsType insertsType) {
598
                var iterator = entities.iterator();
1✔
599
                if (!iterator.hasNext()) {
1✔
600
                        return 0;
1✔
601
                }
602
                var firstEntity = iterator.next();
1✔
603
                @SuppressWarnings("unchecked")
604
                var type = (Class<E>) firstEntity.getClass();
1✔
605
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
606
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
607
                return inserts(type, stream, condition, insertsType);
1✔
608
        }
609

610
        /**
611
         * {@inheritDoc}
612
         *
613
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream)
614
         */
615
        @Override
616
        public <E> int inserts(final Stream<E> entities) {
617
                return inserts(entities, getInsertsCondition(getInsertsType()));
1✔
618
        }
619

620
        /**
621
         * {@inheritDoc}
622
         *
623
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
624
         */
625
        @Override
626
        public <E> int inserts(final Stream<E> entities,
627
                        final InsertsCondition<? super E> condition) {
628
                return inserts(entities, condition, getInsertsType());
1✔
629
        }
630

631
        /**
632
         * {@inheritDoc}
633
         *
634
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
635
         */
636
        @Override
637
        public <E> int inserts(final Stream<E> entities,
638
                        final InsertsType insertsType) {
639
                return inserts(entities, getInsertsCondition(insertsType), insertsType);
1✔
640
        }
641

642
        /**
643
         * {@inheritDoc}
644
         *
645
         * @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)
646
         */
647
        @Override
648
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
649
                        final Stream<E> entities,
650
                        final InsertsCondition<? super E> condition,
651
                        final InsertsType insertsType) {
652
                var insertedEntities = new ArrayList<E>();
1✔
653
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
654
                        bulkInsert(entityType, entities, condition, insertedEntities);
1✔
655
                } else {
656
                        batchInsert(entityType, entities, condition, insertedEntities);
1✔
657
                }
658
                return insertedEntities.stream();
1✔
659
        }
660

661
        /**
662
         * {@inheritDoc}
663
         *
664
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
665
         */
666
        @Override
667
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
668
                        final Stream<E> entities,
669
                        final InsertsCondition<? super E> condition) {
670
                return insertsAndReturn(entityType, entities, condition, getInsertsType());
1✔
671
        }
672

673
        /**
674
         * {@inheritDoc}
675
         *
676
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream)
677
         */
678
        @Override
679
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
680
                        final Stream<E> entities) {
681
                return insertsAndReturn(entityType, entities, getInsertsCondition(getInsertsType()));
1✔
682
        }
683

684
        /**
685
         * {@inheritDoc}
686
         *
687
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
688
         */
689
        @Override
690
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
691
                        final Stream<E> entities,
692
                        final InsertsType insertsType) {
693
                return insertsAndReturn(entityType, entities, getInsertsCondition(insertsType), insertsType);
1✔
694
        }
695

696
        /**
697
         * {@inheritDoc}
698
         *
699
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
700
         */
701
        @Override
702
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
703
                        final InsertsCondition<? super E> condition,
704
                        final InsertsType insertsType) {
705
                var iterator = entities.iterator();
1✔
706
                if (!iterator.hasNext()) {
1✔
707
                        return Stream.empty();
1✔
708
                }
709
                var firstEntity = iterator.next();
1✔
710
                @SuppressWarnings("unchecked")
711
                var type = (Class<E>) firstEntity.getClass();
1✔
712
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
713
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
714
                return insertsAndReturn(type, stream, condition, insertsType);
1✔
715
        }
716

717
        /**
718
         * {@inheritDoc}
719
         *
720
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
721
         */
722
        @Override
723
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
724
                        final InsertsCondition<? super E> condition) {
725
                return insertsAndReturn(entities, condition, getInsertsType());
1✔
726
        }
727

728
        /**
729
         * {@inheritDoc}
730
         *
731
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream)
732
         */
733
        @Override
734
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities) {
735
                return insertsAndReturn(entities, getInsertsCondition(getInsertsType()));
1✔
736
        }
737

738
        /**
739
         * {@inheritDoc}
740
         *
741
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
742
         */
743
        @Override
744
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
745
                        final InsertsType insertsType) {
746
                return insertsAndReturn(entities, getInsertsCondition(insertsType), insertsType);
1✔
747
        }
748

749
        /**
750
         * {@inheritDoc}
751
         *
752
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
753
         */
754
        @Override
755
        public <E> int updates(final Class<E> entityType,
756
                        final Stream<E> entities,
757
                        final UpdatesCondition<? super E> condition) {
758
                return batchUpdate(entityType, entities, condition, null);
1✔
759
        }
760

761
        /**
762
         * {@inheritDoc}
763
         *
764
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
765
         */
766
        @Override
767
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
768
                        final Stream<E> entities,
769
                        final UpdatesCondition<? super E> condition) {
770
                var updatedEntities = new ArrayList<E>();
1✔
771
                batchUpdate(entityType, entities, condition, updatedEntities);
1✔
772
                return updatedEntities.stream();
1✔
773
        }
774

775
        /**
776
         * {@inheritDoc}
777
         *
778
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.lang.Class, java.util.stream.Stream)
779
         */
780
        @Override
781
        public <E> int updates(final Class<E> entityType,
782
                        final Stream<E> entities) {
783
                return updates(entityType, entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
784
        }
785

786
        /**
787
         * {@inheritDoc}
788
         *
789
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream)
790
         */
791
        @Override
792
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
793
                        final Stream<E> entities) {
794
                return updatesAndReturn(entityType, entities, DEFAULT_UPDATES_WHEN_CONDITION);
×
795
        }
796

797
        /**
798
         * {@inheritDoc}
799
         *
800
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
801
         */
802
        @Override
803
        public <E> int updates(final Stream<E> entities,
804
                        final UpdatesCondition<? super E> condition) {
805
                var iterator = entities.iterator();
1✔
806
                if (!iterator.hasNext()) {
1✔
807
                        return 0;
×
808
                }
809
                var firstEntity = iterator.next();
1✔
810
                @SuppressWarnings("unchecked")
811
                var type = (Class<E>) firstEntity.getClass();
1✔
812
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
813
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
814
                return updates(type, stream, condition);
1✔
815
        }
816

817
        /**
818
         * {@inheritDoc}
819
         *
820
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
821
         */
822
        @Override
823
        public <E> Stream<E> updatesAndReturn(final Stream<E> entities,
824
                        final UpdatesCondition<? super E> condition) {
825
                var iterator = entities.iterator();
1✔
826
                if (!iterator.hasNext()) {
1✔
827
                        return Stream.empty();
×
828
                }
829
                var firstEntity = iterator.next();
1✔
830
                @SuppressWarnings("unchecked")
831
                var type = (Class<E>) firstEntity.getClass();
1✔
832
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
833
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
834
                return updatesAndReturn(type, stream, condition);
1✔
835
        }
836

837
        /**
838
         * {@inheritDoc}
839
         *
840
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.util.stream.Stream)
841
         */
842
        @Override
843
        public <E> int updates(final Stream<E> entities) {
844
                return updates(entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
845
        }
846

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

857
        private <E> InsertsCondition<? super E> getInsertsCondition(final InsertsType insertsType) {
858
                return InsertsType.BATCH.equals(insertsType)
1✔
859
                                ? DEFAULT_BATCH_INSERTS_WHEN_CONDITION
1✔
860
                                : DEFAULT_BULK_INSERTS_WHEN_CONDITION;
1✔
861
        }
862

863
        /**
864
         * 複数エンティティのBULK INSERTを実行
865
         *
866
         * @param <E> エンティティの型
867
         * @param entityType エンティティの型
868
         * @param entities エンティティ
869
         * @param condition 一括INSERT用のフレームの判定条件
870
         * @param insertedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
871
         * @return SQL実行結果
872
         */
873
        private <E> int batchInsert(final Class<E> entityType, final Stream<E> entities,
874
                        final InsertsCondition<? super E> condition, final List<E> insertedEntities) {
875
                EntityHandler<E> handler = this.getEntityHandler();
1✔
876
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
877
                        throw new IllegalArgumentException("Entity type not supported");
1✔
878
                }
879

880
                try {
881
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
882
                        var context = handler.createBatchInsertContext(this, metadata, entityType);
1✔
883
                        context.setSqlKind(SqlKind.BATCH_INSERT);
1✔
884

885
                        var count = 0;
1✔
886
                        var entityList = new ArrayList<E>();
1✔
887
                        var isFirst = true;
1✔
888
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
889
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
890
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
891
                                var entity = iterator.next();
1✔
892

893
                                if (!entityType.isInstance(entity)) {
1✔
894
                                        throw new IllegalArgumentException("Entity types do not match");
1✔
895
                                }
896

897
                                if (isFirst) {
1✔
898
                                        isFirst = false;
1✔
899
                                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
900
                                        autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
901

902
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
903
                                        var excludeColumns = autoGeneratedColumns;
1✔
904
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
905
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
906
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
907
                                                                        && col.getValue(entity) != null)
1✔
908
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
909
                                }
910

911
                                entityList.add(entity);
1✔
912
                                if (insertedEntities != null) {
1✔
913
                                        insertedEntities.add(entity);
1✔
914
                                }
915

916
                                handler.setInsertParams(context, entity);
1✔
917
                                context.addBatch();
1✔
918
                                // SQLのID項目IF分岐判定をtrueにするためにaddBatch()の後に保持しておいたID項目をcontextにバインドする
919
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
920
                                        context.paramMap(nonNullObjectIdFlags);
1✔
921
                                }
922
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
923
                                        count += Arrays.stream(doBatchInsert(context, handler, entityList, autoGeneratedColumns)).sum();
1✔
924
                                        entityList.clear();
1✔
925
                                }
926
                        }
1✔
927
                        return count + (context.batchCount() != 0
1✔
928
                                        ? Arrays.stream(doBatchInsert(context, handler, entityList, autoGeneratedColumns)).sum()
1✔
929
                                        : 0);
1✔
930
                } catch (SQLException e) {
×
931
                        throw new EntitySqlRuntimeException(SqlKind.BATCH_INSERT, e);
×
932
                }
933
        }
934

935
        private <E> int[] doBatchInsert(final ExecutionContext context, final EntityHandler<E> handler,
936
                        final List<E> entityList, final List<MappingColumn> autoGeneratedColumns) throws SQLException {
937
                var counts = handler.doBatchInsert(this, context);
1✔
938
                if (!autoGeneratedColumns.isEmpty()) {
1✔
939
                        var ids = context.getGeneratedKeyValues();
1✔
940
                        var idx = 0;
1✔
941
                        for (E ent : entityList) {
1✔
942
                                for (var col : autoGeneratedColumns) {
1✔
943
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
944
                                }
1✔
945
                        }
1✔
946
                }
947

948
                return counts;
1✔
949
        }
950

951
        /**
952
         * 複数エンティティのINSERTをバッチ実行
953
         *
954
         * @param <E> エンティティの型
955
         * @param entityType エンティティの型
956
         * @param entities エンティティ
957
         * @param condition 一括INSERT用のフレームの判定条件
958
         * @param insertedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
959
         * @return SQL実行結果
960
         */
961
        private <E> int bulkInsert(final Class<E> entityType, final Stream<E> entities,
962
                        final InsertsCondition<? super E> condition, final List<E> insertedEntities) {
963
                EntityHandler<E> handler = this.getEntityHandler();
1✔
964
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
965
                        throw new IllegalArgumentException("Entity type not supported");
×
966
                }
967

968
                try {
969
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
970
                        var context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
971
                        context.setSqlKind(SqlKind.BULK_INSERT);
1✔
972

973
                        var frameCount = 0;
1✔
974
                        var count = 0;
1✔
975
                        var isFirst = true;
1✔
976
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
977
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
978
                        var entityList = new ArrayList<E>();
1✔
979
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
980
                                var entity = iterator.next();
1✔
981

982
                                if (!entityType.isInstance(entity)) {
1✔
983
                                        throw new IllegalArgumentException("Entity types do not match");
×
984
                                }
985

986
                                if (isFirst) {
1✔
987
                                        isFirst = false;
1✔
988
                                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
989
                                        autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
990

991
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
992
                                        var excludeColumns = autoGeneratedColumns;
1✔
993
                                        // indexなしのid値がcontextにバインドされないため、値としてkey=trueを退避しておく
994
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
995
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
996
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
997
                                                                        && col.getValue(entity) != null)
1✔
998
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
999
                                }
1000
                                // 退避しておいたid値をこのタイミングで設定する
1001
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
1002
                                        context.paramMap(nonNullObjectIdFlags);
1✔
1003
                                }
1004

1005
                                entityList.add(entity);
1✔
1006
                                if (insertedEntities != null) {
1✔
1007
                                        insertedEntities.add(entity);
1✔
1008
                                }
1009

1010
                                handler.setBulkInsertParams(context, entity, frameCount);
1✔
1011
                                frameCount++;
1✔
1012

1013
                                if (condition.test(context, frameCount, entity)) {
1✔
1014
                                        count += doBulkInsert(context, entityType, handler, metadata, autoGeneratedColumns, entityList);
1✔
1015
                                        frameCount = 0;
1✔
1016
                                        entityList.clear();
1✔
1017

1018
                                        // 新しいExecutionContextを作成する前にgeneratedKeyColumnsを退避しておく
1019
                                        var generatedKeyColumns = context.getGeneratedKeyColumns();
1✔
1020
                                        context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
1021
                                        context.setSqlKind(SqlKind.BULK_INSERT);
1✔
1022
                                        // 実行結果から生成されたIDを取得できるようにPreparedStatementにIDカラムを渡す
1023
                                        context.setGeneratedKeyColumns(generatedKeyColumns);
1✔
1024
                                }
1025
                        }
1✔
1026
                        return count + (frameCount > 0
1✔
1027
                                        ? doBulkInsert(context, entityType, handler, metadata, autoGeneratedColumns, entityList)
1✔
1028
                                        : 0);
1✔
1029

1030
                } catch (SQLException e) {
×
1031
                        throw new EntitySqlRuntimeException(SqlKind.BULK_INSERT, e);
×
1032
                }
1033
        }
1034

1035
        private <E> int doBulkInsert(final ExecutionContext context, final Class<E> entityType,
1036
                        final EntityHandler<E> handler,
1037
                        final TableMetadata metadata, final List<MappingColumn> autoGeneratedColumns, final List<E> entityList)
1038
                        throws SQLException {
1039
                var count = handler.doBulkInsert(this,
1✔
1040
                                handler.setupSqlBulkInsertContext(this, context, metadata, entityType, entityList.size()));
1✔
1041

1042
                if (!autoGeneratedColumns.isEmpty()) {
1✔
1043
                        var ids = context.getGeneratedKeyValues();
1✔
1044
                        var idx = 0;
1✔
1045
                        for (E ent : entityList) {
1✔
1046
                                for (var col : autoGeneratedColumns) {
1✔
1047
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
1048
                                }
1✔
1049
                        }
1✔
1050
                }
1051

1052
                return count;
1✔
1053
        }
1054

1055
        /**
1056
         * 複数エンティティのBULK UPDATEを実行
1057
         *
1058
         * @param <E> エンティティの型
1059
         * @param entityType エンティティの型
1060
         * @param entities エンティティ
1061
         * @param condition 一括更新用のフレームの判定条件
1062
         * @param updatedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
1063
         * @return SQL実行結果
1064
         */
1065
        private <E> int batchUpdate(final Class<E> entityType, final Stream<E> entities,
1066
                        final UpdatesCondition<? super E> condition, final List<E> updatedEntities) {
1067
                var handler = this.getEntityHandler();
1✔
1068
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1069
                        throw new IllegalArgumentException("Entity type not supported");
×
1070
                }
1071

1072
                try {
1073
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1074
                        var context = handler.createBatchUpdateContext(this, metadata, entityType);
1✔
1075
                        context.setSqlKind(SqlKind.BATCH_UPDATE);
1✔
1076

1077
                        var versionColumn = MappingUtils.getVersionMappingColumn(metadata.getSchema(),
1✔
1078
                                        entityType);
1079
                        var entityCount = 0;
1✔
1080
                        var updateCount = 0;
1✔
1081
                        var entityList = new ArrayList<E>();
1✔
1082
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1083
                                var entity = iterator.next();
1✔
1084

1085
                                if (!entityType.isInstance(entity)) {
1✔
1086
                                        throw new IllegalArgumentException("Entity types do not match");
×
1087
                                }
1088

1089
                                entityList.add(entity);
1✔
1090
                                if (updatedEntities != null) {
1✔
1091
                                        updatedEntities.add(entity);
1✔
1092
                                }
1093
                                entityCount++;
1✔
1094

1095
                                handler.setUpdateParams(context, entity);
1✔
1096
                                context.addBatch();
1✔
1097

1098
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
1099
                                        updateCount += Arrays.stream(handler.doBatchUpdate(this, context)).sum();
1✔
1100
                                        entityList.clear();
1✔
1101
                                }
1102
                        }
1✔
1103
                        updateCount = updateCount + (context.batchCount() != 0
1✔
1104
                                        ? Arrays.stream(handler.doBatchUpdate(this, context)).sum()
1✔
1105
                                        : 0);
1✔
1106

1107
                        if (updatedEntities != null && versionColumn.isPresent()) {
1✔
1108
                                var vColumn = versionColumn.get();
1✔
1109
                                var keyColumns = metadata.getColumns().stream()
1✔
1110
                                                .filter(TableMetadata.Column::isKey)
1✔
1111
                                                .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
1112
                                                .map(c -> MappingUtils.getMappingColumnMap(metadata.getSchema(), entityType, SqlKind.NONE)
1✔
1113
                                                                .get(c.getCamelColumnName()))
1✔
1114
                                                .collect(Collectors.toList());
1✔
1115

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

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

1126
                                        for (var start = 0; start < entitySize; start = start + IN_CLAUSE_MAX_PARAM_SIZE) {
1✔
1127
                                                var end = Math.min(start + IN_CLAUSE_MAX_PARAM_SIZE, entitySize);
1✔
1128
                                                var subList = keyList.subList(start, end);
1✔
1129

1130
                                                query(entityType).in(keyColumn.getCamelName(), subList).stream()
1✔
1131
                                                                .map(e -> {
1✔
1132
                                                                        var updatedEntity = updatedEntityMap.get(keyColumn.getValue(e)).get(0);
1✔
1133
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
1✔
1134
                                                                        return updatedEntity;
1✔
1135
                                                                }).count();
1✔
1136
                                        }
1137
                                } else if (keyColumns.size() > 1) {
1✔
1138
                                        // 複合キーの場合はIN句で一括取得できないため1件ずつ取得して@Versionのついたフィールドを更新する
1139
                                        updatedEntities.stream()
1✔
1140
                                                        .map(updatedEntity -> {
1✔
1141
                                                                var keyValues = keyColumns.stream().map(k -> k.getValue(updatedEntity)).toArray();
×
1142
                                                                find(entityType, keyValues).ifPresent(e -> {
×
1143
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
×
1144
                                                                });
×
1145
                                                                return updatedEntity;
×
1146
                                                        }).count();
1✔
1147
                                }
1148
                        }
1149
                        if (versionColumn.isPresent() && getDialect().supportsEntityBulkUpdateOptimisticLock()
1✔
1150
                                        && updateCount != entityCount) {
1151
                                // バージョンカラムの指定があり、更新件数と更新対象Entityの件数が不一致の場合は楽観ロックエラーとする
1152
                                throw new OptimisticLockException(String.format(
1✔
1153
                                                "An error occurred due to optimistic locking.%nExecuted SQL [%n%s]%nBatch Entity Count: %d, Update Count: %d.",
1154
                                                context.getExecutableSql(), entityCount, updateCount));
1✔
1155
                        }
1156
                        return updateCount;
1✔
1157
                } catch (SQLException e) {
×
1158
                        throw new EntitySqlRuntimeException(SqlKind.BATCH_UPDATE, e);
×
1159
                }
1160
        }
1161

1162
        /**
1163
         * {@inheritDoc}
1164
         *
1165
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext)
1166
         */
1167
        @Override
1168
        public ResultSet query(final ExecutionContext executionContext) throws SQLException {
1169
                // パラメータログを出力する
1170
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1171

1172
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1173
                        executionContext.setSqlKind(SqlKind.SELECT);
1✔
1174
                }
1175

1176
                // コンテキスト変換
1177
                transformContext(executionContext);
1✔
1178

1179
                var stmt = getPreparedStatement(executionContext);
1✔
1180

1181
                // INパラメータ設定
1182
                executionContext.bindParams(stmt);
1✔
1183

1184
                Instant startTime = null;
1✔
1185
                if (LOG.isDebugEnabled()) {
1✔
1186
                        LOG.debug("Execute search SQL.");
×
1187
                }
1188
                if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1189
                        startTime = Instant.now(getSqlConfig().getClock());
1✔
1190
                }
1191

1192
                try {
1193
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1194
                        var maxRetryCount = getMaxRetryCount();
1✔
1195
                        if (executionContext.getMaxRetryCount() >= 0) {
1✔
1196
                                maxRetryCount = executionContext.getMaxRetryCount();
1✔
1197
                        }
1198

1199
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1200
                        var retryWaitTime = getRetryWaitTime();
1✔
1201
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1202
                                retryWaitTime = executionContext.getRetryWaitTime();
1✔
1203
                        }
1204
                        var loopCount = 0;
1✔
1205
                        var dialect = getDialect();
1✔
1206
                        ResultSet rs = null;
1✔
1207
                        try {
1208
                                do {
1209
                                        try {
1210
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1211
                                                        setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1212
                                                }
1213
                                                rs = stmt.executeQuery();
1✔
1214
                                                // Query実行後イベント発行
1215
                                                if (getSqlConfig().getEventListenerHolder().hasSqlQueryListener()) {
1✔
1216
                                                        var eventObj = new SqlQueryEvent(executionContext, rs, stmt);
1✔
1217
                                                        for (var listener : getSqlConfig().getEventListenerHolder().getSqlQueryListeners()) {
1✔
1218
                                                                listener.accept(eventObj);
1✔
1219
                                                        }
1✔
1220
                                                        rs = eventObj.getResultSet();
1✔
1221
                                                }
1222
                                                stmt.closeOnCompletion();
1✔
1223
                                                return new InnerResultSet(rs, stmt);
1✔
1224
                                        } catch (SQLException ex) {
1✔
1225
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1226
                                                        rollback(RETRY_SAVEPOINT_NAME);
1✔
1227
                                                }
1228
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1229
                                                var sqlState = ex.getSQLState();
1✔
1230
                                                var pessimisticLockingErrorCodes = dialect.getPessimisticLockingErrorCodes();
1✔
1231
                                                if (maxRetryCount > loopCount
1✔
1232
                                                                && (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState))) {
1✔
1233
                                                        if (LOG.isDebugEnabled()) {
1✔
1234
                                                                LOG.debug(String.format(
×
1235
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1236
                                                                                loopCount + 1, retryWaitTime));
×
1237
                                                        }
1238
                                                        if (retryWaitTime > 0) {
1✔
1239
                                                                try {
1240
                                                                        Thread.sleep(retryWaitTime);
1✔
1241
                                                                } catch (InterruptedException ie) {
×
1242
                                                                        // do nothing
1243
                                                                }
1✔
1244
                                                        }
1245
                                                } else {
1246
                                                        if (pessimisticLockingErrorCodes.contains(errorCode)
1✔
1247
                                                                        || pessimisticLockingErrorCodes.contains(sqlState)) {
1✔
1248
                                                                throw new PessimisticLockException(executionContext, ex);
1✔
1249
                                                        } else {
1250
                                                                throw ex;
1✔
1251
                                                        }
1252
                                                }
1253
                                        } finally {
1254
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1255
                                                        releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1256
                                                }
1257
                                                executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1258
                                        }
1259
                                } while (maxRetryCount > loopCount++);
1✔
1260
                        } catch (SQLException | RuntimeException e) {
1✔
1261
                                if (rs != null && !rs.isClosed()) {
1✔
1262
                                        rs.close();
1✔
1263
                                }
1264
                                throw e;
1✔
1265
                        }
×
1266
                        return null;
×
1267
                } catch (SQLException ex) {
1✔
1268
                        handleException(executionContext, ex);
×
1269
                        return null;
×
1270
                } finally {
1271
                        // 後処理
1272
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1273
                                PERFORMANCE_LOG.info("SQL execution time [{}({})] : [{}]",
1✔
1274
                                                generateSqlName(executionContext),
1✔
1275
                                                executionContext.getSqlKind(),
1✔
1276
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1277
                        }
1278
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1279
                }
1280
        }
1281

1282
        /**
1283
         * {@inheritDoc}
1284
         *
1285
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1286
         *      jp.co.future.uroborosql.converter.ResultSetConverter)
1287
         */
1288
        @Override
1289
        public <T> Stream<T> query(final ExecutionContext executionContext, final ResultSetConverter<T> converter)
1290
                        throws SQLException {
1291
                var rs = query(executionContext);
1✔
1292
                return StreamSupport.stream(new ResultSetSpliterator<>(rs, converter), false).onClose(() -> {
1✔
1293
                        try {
1294
                                if (rs != null && !rs.isClosed()) {
1✔
1295
                                        rs.close();
1✔
1296
                                }
1297
                        } catch (SQLException ex) {
×
1298
                                // do nothing
1299
                        }
1✔
1300
                });
1✔
1301
        }
1302

1303
        /**
1304
         * {@inheritDoc}
1305
         *
1306
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1307
         *      jp.co.future.uroborosql.utils.CaseFormat)
1308
         */
1309
        @Override
1310
        public List<Map<String, Object>> query(final ExecutionContext executionContext, final CaseFormat caseFormat)
1311
                        throws SQLException {
1312
                try (var stream = query(executionContext, new MapResultSetConverter(getSqlConfig(), caseFormat))) {
1✔
1313
                        return stream.collect(Collectors.toList());
1✔
1314
                }
1315
        }
1316

1317
        /**
1318
         * @see jp.co.future.uroborosql.SqlAgent#update(jp.co.future.uroborosql.context.ExecutionContext)
1319
         */
1320
        @Override
1321
        public int update(final ExecutionContext executionContext) throws SQLException {
1322
                // パラメータログを出力する
1323
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1324

1325
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1326
                        executionContext.setSqlKind(SqlKind.UPDATE);
1✔
1327
                }
1328

1329
                // コンテキスト変換
1330
                transformContext(executionContext);
1✔
1331

1332
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1333
                if (executionContext.getUpdateDelegate() != null) {
1✔
1334
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1335
                        LOG.debug("Performs update delegate of update process");
1✔
1336
                        return executionContext.getUpdateDelegate().apply(executionContext);
1✔
1337
                }
1338

1339
                Instant startTime = null;
1✔
1340

1341
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1342

1343
                        // INパラメータ設定
1344
                        executionContext.bindParams(stmt);
1✔
1345

1346
                        if (LOG.isDebugEnabled()) {
1✔
1347
                                LOG.debug("Execute update SQL.");
×
1348
                        }
1349
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1350
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1351
                        }
1352

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

1359
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1360
                        var retryWaitTime = getRetryWaitTime();
1✔
1361
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1362
                                retryWaitTime = executionContext.getRetryWaitTime();
1✔
1363
                        }
1364
                        var loopCount = 0;
1✔
1365
                        do {
1366
                                try {
1367
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1368
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1369
                                        }
1370
                                        var count = stmt.executeUpdate();
1✔
1371
                                        // Update実行後イベント発行
1372
                                        if (getSqlConfig().getEventListenerHolder().hasSqlUpdateListener()) {
1✔
1373
                                                var eventObj = new SqlUpdateEvent(executionContext, count, stmt);
1✔
1374
                                                for (var listener : getSqlConfig().getEventListenerHolder().getSqlUpdateListeners()) {
1✔
1375
                                                        listener.accept(eventObj);
1✔
1376
                                                }
1✔
1377
                                                count = eventObj.getCount();
1✔
1378
                                        }
1379
                                        if ((SqlKind.INSERT.equals(executionContext.getSqlKind()) ||
1✔
1380
                                                        SqlKind.BULK_INSERT.equals(executionContext.getSqlKind()))
1✔
1381
                                                        && executionContext.hasGeneratedKeyColumns()) {
1✔
1382
                                                try (var rs = stmt.getGeneratedKeys()) {
1✔
1383
                                                        var generatedKeyValues = new ArrayList<>();
1✔
1384
                                                        while (rs.next()) {
1✔
1385
                                                                for (var i = 1; i <= executionContext.getGeneratedKeyColumns().length; i++) {
1✔
1386
                                                                        generatedKeyValues.add(rs.getObject(i));
1✔
1387
                                                                }
1388
                                                        }
1389
                                                        executionContext.setGeneratedKeyValues(
1✔
1390
                                                                        generatedKeyValues.toArray(new Object[generatedKeyValues.size()]));
1✔
1391
                                                }
1392
                                        }
1393
                                        return count;
1✔
1394
                                } catch (SQLException ex) {
1✔
1395
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1396
                                                rollback(RETRY_SAVEPOINT_NAME);
1✔
1397
                                        }
1398
                                        if (maxRetryCount > loopCount) {
1✔
1399
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1400
                                                var sqlState = ex.getSQLState();
1✔
1401
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1402
                                                        if (LOG.isDebugEnabled()) {
1✔
1403
                                                                LOG.debug(String.format(
×
1404
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1405
                                                                                loopCount + 1, retryWaitTime));
×
1406
                                                        }
1407
                                                        if (retryWaitTime > 0) {
1✔
1408
                                                                try {
1409
                                                                        Thread.sleep(retryWaitTime);
1✔
1410
                                                                } catch (InterruptedException ie) {
×
1411
                                                                        // do nothing
1412
                                                                }
1✔
1413
                                                        }
1414
                                                } else {
1415
                                                        throw ex;
1✔
1416
                                                }
1417
                                        } else {
1✔
1418
                                                throw ex;
1✔
1419
                                        }
1420
                                } finally {
1421
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1422
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1423
                                        }
1424
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1425
                                }
1426
                        } while (maxRetryCount > loopCount++);
1✔
1427
                        return 0;
×
1428
                } catch (SQLException ex) {
1✔
1429
                        handleException(executionContext, ex);
×
1430
                        return 0;
×
1431
                } finally {
1432
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1433
                                PERFORMANCE_LOG.info("SQL execution time [{}({})] : [{}]",
1✔
1434
                                                generateSqlName(executionContext),
1✔
1435
                                                executionContext.getSqlKind(),
1✔
1436
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1437
                        }
1438
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1439
                }
1440
        }
1441

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

1450
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1451
                        executionContext.setSqlKind(SqlKind.BATCH_INSERT);
1✔
1452
                }
1453

1454
                // コンテキスト変換
1455
                transformContext(executionContext);
1✔
1456

1457
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1458
                if (executionContext.getUpdateDelegate() != null) {
1✔
1459
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1460
                        LOG.debug("Performs update delegate of batch process");
1✔
1461
                        return new int[] { executionContext.getUpdateDelegate().apply(executionContext) };
1✔
1462
                }
1463

1464
                Instant startTime = null;
1✔
1465

1466
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1467

1468
                        // INパラメータ設定
1469
                        executionContext.bindBatchParams(stmt);
1✔
1470

1471
                        if (LOG.isDebugEnabled()) {
1✔
1472
                                LOG.debug("Execute batch process.");
×
1473
                        }
1474
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1475
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1476
                        }
1477

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

1484
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1485
                        var retryWaitTime = getRetryWaitTime();
1✔
1486
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1487
                                retryWaitTime = executionContext.getRetryWaitTime();
×
1488
                        }
1489
                        var loopCount = 0;
1✔
1490
                        do {
1491
                                try {
1492
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1493
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
×
1494
                                        }
1495
                                        var counts = stmt.executeBatch();
1✔
1496
                                        // Batch実行後イベント発行
1497
                                        if (getSqlConfig().getEventListenerHolder().hasSqlBatchListener()) {
1✔
1498
                                                var eventObj = new SqlBatchEvent(executionContext, counts, stmt);
1✔
1499
                                                for (var listener : getSqlConfig().getEventListenerHolder().getSqlBatchListeners()) {
1✔
1500
                                                        listener.accept(eventObj);
1✔
1501
                                                }
1✔
1502
                                                counts = eventObj.getCounts();
1✔
1503
                                        }
1504
                                        if (SqlKind.BATCH_INSERT.equals(executionContext.getSqlKind())
1✔
1505
                                                        && executionContext.hasGeneratedKeyColumns()) {
1✔
1506
                                                try (var rs = stmt.getGeneratedKeys()) {
1✔
1507
                                                        var generatedKeyValues = new ArrayList<>();
1✔
1508
                                                        while (rs.next()) {
1✔
1509
                                                                for (var i = 1; i <= executionContext.getGeneratedKeyColumns().length; i++) {
1✔
1510
                                                                        generatedKeyValues.add(rs.getObject(i));
1✔
1511
                                                                }
1512
                                                        }
1513
                                                        executionContext.setGeneratedKeyValues(
1✔
1514
                                                                        generatedKeyValues.toArray(new Object[generatedKeyValues.size()]));
1✔
1515
                                                }
1516
                                        }
1517
                                        return counts;
1✔
1518
                                } catch (SQLException ex) {
1✔
1519
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1520
                                                rollback(RETRY_SAVEPOINT_NAME);
×
1521
                                        }
1522
                                        if (maxRetryCount > loopCount) {
1✔
1523
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1524
                                                var sqlState = ex.getSQLState();
1✔
1525
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1526
                                                        if (LOG.isDebugEnabled()) {
1✔
1527
                                                                LOG.debug(String.format(
×
1528
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1529
                                                                                loopCount + 1, retryWaitTime));
×
1530
                                                        }
1531
                                                        if (retryWaitTime > 0) {
1✔
1532
                                                                try {
1533
                                                                        Thread.sleep(retryWaitTime);
×
1534
                                                                } catch (InterruptedException ie) {
×
1535
                                                                        // do nothing
1536
                                                                }
×
1537
                                                        }
1538
                                                } else {
1539
                                                        throw ex;
×
1540
                                                }
1541
                                        } else {
1✔
1542
                                                throw ex;
1✔
1543
                                        }
1544
                                } finally {
1545
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1546
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
×
1547
                                        }
1548
                                        executionContext.clearBatch();
1✔
1549
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1550
                                }
1551
                        } while (maxRetryCount > loopCount++);
1✔
1552
                        return null;
×
1553
                } catch (SQLException ex) {
1✔
1554
                        handleException(executionContext, ex);
×
1555
                        return null;
×
1556
                } finally {
1557
                        // 後処理
1558
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1559
                                PERFORMANCE_LOG.info("SQL execution time [{}({})] : [{}]",
1✔
1560
                                                generateSqlName(executionContext),
1✔
1561
                                                executionContext.getSqlKind(),
1✔
1562
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1563
                        }
1564
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1565
                }
1566
        }
1567

1568
        /**
1569
         * {@inheritDoc}
1570
         *
1571
         * @see jp.co.future.uroborosql.SqlAgent#procedure(jp.co.future.uroborosql.context.ExecutionContext)
1572
         */
1573
        @Override
1574
        public Map<String, Object> procedure(final ExecutionContext executionContext) throws SQLException {
1575
                // パラメータログを出力する
1576
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1577

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

1581
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1582
                        executionContext.setSqlKind(SqlKind.PROCEDURE);
1✔
1583
                }
1584

1585
                // コンテキスト変換
1586
                transformContext(executionContext);
1✔
1587

1588
                Instant startTime = null;
1✔
1589

1590
                try (var callableStatement = getCallableStatement(executionContext)) {
1✔
1591

1592
                        // パラメータ設定
1593
                        executionContext.bindParams(callableStatement);
1✔
1594

1595
                        if (LOG.isDebugEnabled()) {
1✔
1596
                                LOG.debug("Execute stored procedure.");
×
1597
                        }
1598
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1599
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1600
                        }
1601

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

1608
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1609
                        var retryWaitTime = getRetryWaitTime();
1✔
1610
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1611
                                retryWaitTime = executionContext.getRetryWaitTime();
1✔
1612
                        }
1613
                        var loopCount = 0;
1✔
1614
                        do {
1615
                                try {
1616
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1617
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1618
                                        }
1619
                                        var result = callableStatement.execute();
1✔
1620
                                        // Procedure実行後イベント発行
1621
                                        if (getSqlConfig().getEventListenerHolder().hasProcedureListener()) {
1✔
1622
                                                var eventObj = new ProcedureEvent(executionContext, result, callableStatement);
1✔
1623
                                                for (var listener : getSqlConfig().getEventListenerHolder().getProcedureListeners()) {
1✔
1624
                                                        listener.accept(eventObj);
1✔
1625
                                                }
1✔
1626
                                                result = eventObj.isResult();
1✔
1627
                                        }
1628
                                        break;
1629
                                } catch (SQLException ex) {
1✔
1630
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1631
                                                rollback(RETRY_SAVEPOINT_NAME);
1✔
1632
                                        }
1633
                                        if (maxRetryCount > loopCount) {
1✔
1634
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1635
                                                var sqlState = ex.getSQLState();
1✔
1636
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1637
                                                        if (LOG.isDebugEnabled()) {
1✔
1638
                                                                LOG.debug(String.format(
×
1639
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1640
                                                                                loopCount + 1, retryWaitTime));
×
1641
                                                        }
1642
                                                        if (retryWaitTime > 0) {
1✔
1643
                                                                try {
1644
                                                                        Thread.sleep(retryWaitTime);
1✔
1645
                                                                } catch (InterruptedException ie) {
×
1646
                                                                        // do nothing
1647
                                                                }
1✔
1648
                                                        }
1649
                                                } else {
1650
                                                        throw ex;
1✔
1651
                                                }
1652
                                        } else {
1✔
1653
                                                throw ex;
1✔
1654
                                        }
1655
                                } finally {
1656
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1657
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1658
                                        }
1659
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1660
                                }
1661
                        } while (maxRetryCount > loopCount++);
1✔
1662
                        // 結果取得
1663
                        return executionContext.getOutParams(callableStatement);
1✔
1664
                } catch (SQLException ex) {
1✔
1665
                        handleException(executionContext, ex);
×
1666
                } finally {
1667
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1668
                                PERFORMANCE_LOG.info("Stored procedure execution time [{}({})] : [{}]",
1✔
1669
                                                generateSqlName(executionContext),
1✔
1670
                                                executionContext.getSqlKind(),
1✔
1671
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1672
                        }
1673
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1674
                }
1675
                return null;
×
1676
        }
1677

1678
        /**
1679
         * ExecutionContextの設定内容を元にSQLを構築する
1680
         *
1681
         * @param executionContext ExecutionContext
1682
         */
1683
        private void transformContext(final ExecutionContext executionContext) {
1684
                var originalSql = executionContext.getSql();
1✔
1685
                var sqlName = executionContext.getSqlName();
1✔
1686
                if (StringUtils.isEmpty(originalSql) && getSqlResourceManager() != null) {
1✔
1687
                        originalSql = getSqlResourceManager().getSql(sqlName);
1✔
1688
                        if (StringUtils.isEmpty(originalSql)) {
1✔
1689
                                throw new UroborosqlRuntimeException("sql file:[" + sqlName + "] is not found.");
×
1690
                        }
1691
                }
1692

1693
                // SQL-IDの付与
1694
                if (originalSql.contains(keySqlId)) {
1✔
1695
                        var sqlId = executionContext.getSqlId();
1✔
1696
                        if (StringUtils.isEmpty(sqlId)) {
1✔
1697
                                sqlId = sqlName;
1✔
1698
                        }
1699
                        if (StringUtils.isEmpty(sqlId)) {
1✔
1700
                                sqlId = String.valueOf(originalSql.hashCode());
×
1701
                        }
1702

1703
                        originalSql = originalSql.replace(keySqlId, sqlId);
1✔
1704
                }
1705

1706
                // SQL変換前イベント発行
1707
                if (getSqlConfig().getEventListenerHolder().hasTransformSqlListener()) {
1✔
1708
                        var eventObj = new TransformSqlEvent(executionContext, originalSql);
1✔
1709
                        getSqlConfig().getEventListenerHolder().getTransformSqlListeners()
1✔
1710
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1711
                        originalSql = eventObj.getSql();
1✔
1712
                }
1713
                executionContext.setSql(originalSql);
1✔
1714

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

1718
                // SQLパース前イベントの呼出
1719
                if (executionContext.batchCount() == 0
1✔
1720
                                && getSqlConfig().getEventListenerHolder().hasBeforeParseSqlListener()) {
1✔
1721
                        var eventObj = new BeforeParseSqlEvent(executionContext);
1✔
1722
                        getSqlConfig().getEventListenerHolder().getBeforeParseSqlListeners()
1✔
1723
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1724
                }
1725

1726
                if (StringUtils.isEmpty(executionContext.getExecutableSql())) {
1✔
1727
                        // SQLパーサーによるパース処理
1728
                        var outputBindComment = (boolean) executionContext.contextAttrs().getOrDefault(
1✔
1729
                                        CTX_ATTR_KEY_OUTPUT_BIND_COMMENT, true);
1✔
1730
                        var sqlParser = new SqlParserImpl(originalSql, sqlConfig.getExpressionParser(),
1✔
1731
                                        getDialect().isRemoveTerminator(), outputBindComment);
1✔
1732
                        var contextTransformer = sqlParser.parse();
1✔
1733
                        contextTransformer.transform(executionContext);
1✔
1734

1735
                        if (coverageHandlerRef.get() != null) {
1✔
1736
                                // SQLカバレッジ用のログを出力する
1737
                                var coverageData = new CoverageData(sqlName, originalSql,
1✔
1738
                                                contextTransformer.getPassedRoute());
1✔
1739
                                COVERAGE_LOG.info("{}", coverageData);
1✔
1740

1741
                                coverageHandlerRef.get().accept(coverageData);
1✔
1742
                        }
1743
                }
1744

1745
                if (SQL_LOG.isInfoEnabled() && sqlName != null) {
1✔
1746
                        if (getSqlResourceManager().existSql(sqlName)) {
1✔
1747
                                SQL_LOG.info("SQLPath : {}", getSqlResourceManager().getSqlPath(sqlName));
1✔
1748
                        } else {
1749
                                SQL_LOG.info("EntityClass : {}", sqlName);
1✔
1750
                        }
1751
                }
1752
                if (SQL_LOG.isDebugEnabled()) {
1✔
1753
                        SQL_LOG.debug("Template SQL[{}{}{}]", System.lineSeparator(), originalSql, System.lineSeparator());
×
1754
                }
1755
                if (SQL_LOG.isInfoEnabled()) {
1✔
1756
                        SQL_LOG.info("Executed SQL[{}{}{}]", System.lineSeparator(), executionContext.getExecutableSql(),
1✔
1757
                                        System.lineSeparator());
1✔
1758
                }
1759
        }
1✔
1760

1761
        /** 時間計測用のログに出力するSQL名を生成する.
1762
         *
1763
         * @param executionContext ExecutionContext
1764
         * @return SQL名. SQL名が取得できない場合はSQL_ID、または空文字を返却する
1765
         */
1766
        private String generateSqlName(final ExecutionContext executionContext) {
1767
                if (executionContext.getSqlName() != null) {
1✔
1768
                        return executionContext.getSqlName();
1✔
1769
                } else {
1770
                        return Objects.toString(executionContext.getSqlId(), "");
1✔
1771
                }
1772
        }
1773

1774
        /**
1775
         * 例外発生時ハンドラー
1776
         *
1777
         * @param executionContext ExecutionContext
1778
         * @param ex SQL例外
1779
         * @throws SQLException SQL例外
1780
         */
1781
        private void handleException(final ExecutionContext executionContext, final SQLException ex) throws SQLException {
1782
                var cause = ex;
1✔
1783

1784
                while (cause.getNextException() != null) {
1✔
1785
                        cause = cause.getNextException();
1✔
1786
                }
1787

1788
                if (LOG.isErrorEnabled() && outputExceptionLog) {
1✔
1789
                        var builder = new StringBuilder();
1✔
1790
                        builder.append(System.lineSeparator()).append("Exception occurred in SQL execution.")
1✔
1791
                                        .append(System.lineSeparator());
1✔
1792
                        builder.append("Executed SQL[").append(executionContext.getExecutableSql()).append("]")
1✔
1793
                                        .append(System.lineSeparator());
1✔
1794
                        if (executionContext instanceof ExecutionContextImpl) {
1✔
1795
                                var bindParameters = ((ExecutionContextImpl) executionContext).getBindParameters();
1✔
1796
                                for (var i = 0; i < bindParameters.length; i++) {
1✔
1797
                                        var parameter = bindParameters[i];
1✔
1798
                                        builder.append("Bind Parameter.[INDEX[").append(i + 1).append("], ").append(parameter.toString())
1✔
1799
                                                        .append("]").append(System.lineSeparator());
1✔
1800
                                }
1801
                        }
1802
                        LOG.error(builder.toString(), cause);
1✔
1803
                }
1804

1805
                throw cause;
1✔
1806
        }
1807

1808
        /**
1809
         *
1810
         * {@inheritDoc}
1811
         *
1812
         * @see jp.co.future.uroborosql.SqlAgent#find(java.lang.Class, java.lang.Object[])
1813
         */
1814
        @SuppressWarnings("unchecked")
1815
        @Override
1816
        public <E> Optional<E> find(final Class<? extends E> entityType, final Object... keys) {
1817
                @SuppressWarnings("rawtypes")
1818
                EntityHandler handler = this.getEntityHandler();
1✔
1819
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1820
                        throw new IllegalArgumentException("Entity type not supported");
×
1821
                }
1822

1823
                try {
1824
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1825
                        var keyNames = metadata.getColumns().stream()
1✔
1826
                                        .filter(TableMetadata.Column::isKey)
1✔
1827
                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
1828
                                        .map(TableMetadata.Column::getColumnName)
1✔
1829
                                        .map(CaseFormat.CAMEL_CASE::convert)
1✔
1830
                                        .toArray(String[]::new);
1✔
1831

1832
                        if (keyNames.length != keys.length) {
1✔
1833
                                throw new IllegalArgumentException("Number of keys does not match");
×
1834
                        }
1835
                        var params = new HashMap<String, Object>();
1✔
1836
                        for (var i = 0; i < keys.length; i++) {
1✔
1837
                                params.put(keyNames[i], keys[i]);
1✔
1838
                        }
1839

1840
                        var context = handler.createSelectContext(this, metadata, entityType, true);
1✔
1841
                        context.paramMap(params);
1✔
1842

1843
                        try (var stream = handler.doSelect(this, context, entityType)) {
1✔
1844
                                return stream.findFirst();
1✔
1845
                        }
1846
                } catch (SQLException e) {
×
1847
                        throw new EntitySqlRuntimeException(SqlKind.SELECT, e);
×
1848
                }
1849
        }
1850

1851
        /**
1852
         * {@inheritDoc}
1853
         *
1854
         * @see jp.co.future.uroborosql.SqlAgent#insert(java.lang.Object)
1855
         */
1856
        @SuppressWarnings("unchecked")
1857
        @Override
1858
        public <E> int insert(final E entity) {
1859
                if (entity instanceof Stream) {
1✔
1860
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
1861
                }
1862

1863
                @SuppressWarnings("rawtypes")
1864
                EntityHandler handler = this.getEntityHandler();
1✔
1865
                if (!handler.getEntityType().isInstance(entity)) {
1✔
1866
                        throw new IllegalArgumentException("Entity type not supported");
×
1867
                }
1868

1869
                try {
1870
                        var entityType = entity.getClass();
1✔
1871
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1872
                        var context = handler.createInsertContext(this, metadata, entityType);
1✔
1873
                        context.setSqlKind(SqlKind.INSERT);
1✔
1874

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

1879
                        handler.setInsertParams(context, entity);
1✔
1880
                        var count = handler.doInsert(this, context, entity);
1✔
1881

1882
                        if (!autoGeneratedColumns.isEmpty()) {
1✔
1883
                                var ids = context.getGeneratedKeyValues();
1✔
1884
                                var idx = 0;
1✔
1885
                                for (var col : autoGeneratedColumns) {
1✔
1886
                                        setEntityIdValue(entity, ids[idx++], col);
1✔
1887
                                }
1✔
1888
                        }
1889

1890
                        return count;
1✔
1891
                } catch (SQLException e) {
1✔
1892
                        throw new EntitySqlRuntimeException(SqlKind.INSERT, e);
1✔
1893
                }
1894
        }
1895

1896
        /**
1897
         * {@inheritDoc}
1898
         *
1899
         * @see jp.co.future.uroborosql.SqlAgent#insertAndReturn(java.lang.Object)
1900
         */
1901
        @Override
1902
        public <E> E insertAndReturn(final E entity) {
1903
                insert(entity);
1✔
1904
                return entity;
1✔
1905
        }
1906

1907
        /**
1908
         * {@inheritDoc}
1909
         *
1910
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.Object)
1911
         */
1912
        @SuppressWarnings("unchecked")
1913
        @Override
1914
        public <E> int update(final E entity) {
1915
                if (entity instanceof Stream) {
1✔
1916
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
1917
                }
1918

1919
                @SuppressWarnings("rawtypes")
1920
                EntityHandler handler = this.getEntityHandler();
1✔
1921
                if (!handler.getEntityType().isInstance(entity)) {
1✔
1922
                        throw new IllegalArgumentException("Entity type not supported");
×
1923
                }
1924

1925
                try {
1926
                        var type = entity.getClass();
1✔
1927
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
1928
                        var context = handler.createUpdateContext(this, metadata, type, true);
1✔
1929
                        context.setSqlKind(SqlKind.UPDATE);
1✔
1930
                        handler.setUpdateParams(context, entity);
1✔
1931
                        var count = handler.doUpdate(this, context, entity);
1✔
1932

1933
                        MappingUtils.getVersionMappingColumn(metadata.getSchema(), type).ifPresent(versionColumn -> {
1✔
1934
                                if (count == 0) {
1✔
1935
                                        throw new OptimisticLockException(context);
1✔
1936
                                } else {
1937
                                        var columnMap = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.NONE);
1✔
1938
                                        var keys = metadata.getColumns().stream()
1✔
1939
                                                        .filter(TableMetadata.Column::isKey)
1✔
1940
                                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
1941
                                                        .map(c -> columnMap.get(c.getCamelColumnName()).getValue(entity))
1✔
1942
                                                        .toArray();
1✔
1943

1944
                                        find(type, keys).ifPresent(e -> versionColumn.setValue(entity, versionColumn.getValue(e)));
1✔
1945
                                }
1946
                        });
1✔
1947
                        return count;
1✔
1948
                } catch (SQLException e) {
×
1949
                        throw new EntitySqlRuntimeException(SqlKind.UPDATE, e);
×
1950
                }
1951
        }
1952

1953
        /**
1954
         * {@inheritDoc}
1955
         *
1956
         * @see jp.co.future.uroborosql.SqlAgent#updateAndReturn(java.lang.Object)
1957
         */
1958
        @Override
1959
        public <E> E updateAndReturn(final E entity) {
1960
                update(entity);
1✔
1961
                return entity;
1✔
1962
        }
1963

1964
        /**
1965
         * {@inheritDoc}
1966
         *
1967
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class)
1968
         */
1969
        @Override
1970
        public <E> SqlEntityUpdate<E> update(final Class<? extends E> entityType) {
1971
                var handler = this.getEntityHandler();
1✔
1972
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1973
                        throw new IllegalArgumentException("Entity type not supported");
×
1974
                }
1975

1976
                try {
1977
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1978

1979
                        var context = handler.createUpdateContext(this, metadata, entityType, false);
1✔
1980
                        context.setSqlKind(SqlKind.UPDATE);
1✔
1981

1982
                        return new SqlEntityUpdateImpl<>(this, handler, metadata, context);
1✔
1983
                } catch (SQLException e) {
×
1984
                        throw new EntitySqlRuntimeException(SqlKind.DELETE, e);
×
1985
                }
1986
        }
1987

1988
        /**
1989
         * {@inheritDoc}
1990
         *
1991
         * @see jp.co.future.uroborosql.SqlAgent#merge(java.lang.Object)
1992
         */
1993
        @Override
1994
        public <E> int merge(final E entity) {
1995
                mergeAndReturn(entity);
1✔
1996
                return 1;
1✔
1997
        }
1998

1999
        /**
2000
         * {@inheritDoc}
2001
         *
2002
         * @see jp.co.future.uroborosql.SqlAgent#mergeAndReturn(java.lang.Object)
2003
         */
2004
        @Override
2005
        public <E> E mergeAndReturn(final E entity) {
2006
                return doMergeAndReturn(entity, false);
1✔
2007
        }
2008

2009
        /**
2010
         * {@inheritDoc}
2011
         *
2012
         * @see jp.co.future.uroborosql.SqlAgent#mergeWithLocking(java.lang.Object)
2013
         */
2014
        @Override
2015
        public <E> int mergeWithLocking(final E entity) {
2016
                mergeWithLockingAndReturn(entity);
1✔
2017
                return 1;
1✔
2018
        }
2019

2020
        /**
2021
         * {@inheritDoc}
2022
         *
2023
         * @see jp.co.future.uroborosql.SqlAgent#mergeWithLockingAndReturn(java.lang.Object)
2024
         */
2025
        @Override
2026
        public <E> E mergeWithLockingAndReturn(final E entity) {
2027
                return doMergeAndReturn(entity, true);
1✔
2028
        }
2029

2030
        /**
2031
         * エンティティのMERGE(INSERTまたはUPDATE)を実行し、MERGEしたエンティティを返却する.<br>
2032
         * locking引数が<code>true</code>の場合、マージの最初に発行するEntityの検索で悲観ロック(forUpdateNoWait)を行う
2033
         *
2034
         * @param <E> エンティティ型
2035
         * @param entity エンティティ
2036
         * @param locking エンティティの悲観ロックを行うかどうか
2037
         * @return MERGEしたエンティティ
2038
         *
2039
         */
2040
        @SuppressWarnings("unchecked")
2041
        private <E> E doMergeAndReturn(final E entity, final boolean locking) {
2042
                if (entity instanceof Stream) {
1✔
2043
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2044
                }
2045

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

2051
                var type = entity.getClass();
1✔
2052
                try {
2053
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
2054
                        var keyColumns = metadata.getKeyColumns();
1✔
2055

2056
                        if (keyColumns.isEmpty()) {
1✔
2057
                                throw new IllegalArgumentException("Entity has no keys.");
×
2058
                        }
2059

2060
                        var mappingColumns = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.UPDATE);
1✔
2061
                        var query = (SqlEntityQuery<E>) query(type);
1✔
2062
                        for (var column : keyColumns) {
1✔
2063
                                var camelName = column.getCamelColumnName();
1✔
2064
                                query.equal(camelName, mappingColumns.get(camelName).getValue(entity));
1✔
2065
                        }
1✔
2066
                        if (locking) {
1✔
2067
                                query.forUpdateNoWait();
1✔
2068
                        }
2069
                        return query.first()
1✔
2070
                                        .map(findEntity -> {
1✔
2071
                                                for (var mappingColumn : mappingColumns.values()) {
1✔
2072
                                                        if (!mappingColumn.isId()) {
1✔
2073
                                                                var value = mappingColumn.getValue(entity);
1✔
2074
                                                                if (value != null) {
1✔
2075
                                                                        mappingColumn.setValue(findEntity, value);
1✔
2076
                                                                }
2077
                                                        }
2078
                                                }
1✔
2079
                                                return updateAndReturn(findEntity);
1✔
2080
                                        }).orElseGet(() -> insertAndReturn(entity));
1✔
2081
                } catch (SQLException e) {
×
2082
                        throw new EntitySqlRuntimeException(SqlKind.MERGE, e);
×
2083
                }
2084
        }
2085

2086
        /**
2087
         * {@inheritDoc}
2088
         *
2089
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Object)
2090
         */
2091
        @Override
2092
        public <E> int delete(final E entity) {
2093
                if (entity instanceof Stream) {
1✔
2094
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2095
                }
2096

2097
                var handler = this.getEntityHandler();
1✔
2098
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2099
                        throw new IllegalArgumentException("Entity type not supported");
×
2100
                }
2101

2102
                try {
2103
                        var type = entity.getClass();
1✔
2104
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
2105
                        var context = handler.createDeleteContext(this, metadata, type, true);
1✔
2106
                        context.setSqlKind(SqlKind.DELETE);
1✔
2107
                        handler.setDeleteParams(context, entity);
1✔
2108
                        return handler.doDelete(this, context, entity);
1✔
2109
                } catch (SQLException e) {
×
2110
                        throw new EntitySqlRuntimeException(SqlKind.DELETE, e);
×
2111
                }
2112
        }
2113

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

2125
        /**
2126
         * {@inheritDoc}
2127
         *
2128
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class, java.lang.Object[])
2129
         */
2130
        @Override
2131
        public <E> int delete(final Class<? extends E> entityType, final Object... keys) {
2132
                var handler = this.getEntityHandler();
1✔
2133
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2134
                        throw new IllegalArgumentException("Entity type not supported");
×
2135
                }
2136

2137
                try {
2138
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2139
                        TableMetadata.Column keyColumn = null;
1✔
2140
                        var keyColumns = metadata.getKeyColumns();
1✔
2141
                        if (keyColumns.size() == 1) {
1✔
2142
                                keyColumn = keyColumns.get(0);
1✔
2143
                        } else if (keyColumns.isEmpty()) {
1✔
2144
                                keyColumn = metadata.getColumns().get(0);
1✔
2145
                        } else {
2146
                                throw new IllegalArgumentException("Entity has multiple keys");
1✔
2147
                        }
2148
                        return delete(entityType).in(keyColumn.getCamelColumnName(), keys).count();
1✔
2149
                } catch (SQLException e) {
×
2150
                        throw new EntitySqlRuntimeException(SqlKind.DELETE, e);
×
2151
                }
2152
        }
2153

2154
        /**
2155
         * {@inheritDoc}
2156
         *
2157
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class)
2158
         */
2159
        @Override
2160
        public <E> SqlEntityDelete<E> delete(final Class<? extends E> entityType) {
2161
                var handler = this.getEntityHandler();
1✔
2162
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2163
                        throw new IllegalArgumentException("Entity type not supported");
×
2164
                }
2165

2166
                try {
2167
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2168
                        var context = handler.createDeleteContext(this, metadata, entityType, false);
1✔
2169
                        context.setSqlKind(SqlKind.DELETE);
1✔
2170
                        return new SqlEntityDeleteImpl<>(this, handler, metadata, context);
1✔
2171
                } catch (SQLException e) {
×
2172
                        throw new EntitySqlRuntimeException(SqlKind.DELETE, e);
×
2173
                }
2174
        }
2175

2176
        /**
2177
         * {@inheritDoc}
2178
         *
2179
         * @see jp.co.future.uroborosql.SqlAgent#truncate(java.lang.Class)
2180
         */
2181
        @Override
2182
        public <E> SqlAgent truncate(final Class<? extends E> entityType) {
2183
                var handler = this.getEntityHandler();
1✔
2184
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2185
                        throw new IllegalArgumentException("Entity type not supported");
×
2186
                }
2187
                try {
2188
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2189
                        var context = this.context().setSql("truncate table " + metadata.getTableIdentifier());
1✔
2190
                        context.setSqlKind(SqlKind.TRUNCATE);
1✔
2191
                        update(context);
1✔
2192
                        return this;
1✔
2193
                } catch (SQLException e) {
1✔
2194
                        throw new EntitySqlRuntimeException(SqlKind.TRUNCATE, e);
1✔
2195
                }
2196
        }
2197

2198
        /**
2199
         * 自動採番カラムを取得する. 合わせて、{@link ExecutionContext#setGeneratedKeyColumns(String[])} に自動採番カラムを設定する.
2200
         * <pre>
2201
         * 自動採番カラムの条件
2202
         * 1. {@link MappingColumn#isId()} == true 、もしくは{@link TableMetadata.Column#isAutoincrement()} == true であること
2203
         * 2. 1.に加え、エンティティの対象フィールド型がprimitive型、もしくはフィールドの値が<code>null</code>であること
2204
         * </pre>
2205
         *
2206
         * @param <E> entity
2207
         * @param context ExecutionContext
2208
         * @param mappingColumns EntityのMappingColumn配列
2209
         * @param metadata TableMetadata
2210
         * @param entity Entity
2211
         * @return 自動採番カラム配列
2212
         */
2213
        private <E> List<MappingColumn> getAutoGeneratedColumns(final ExecutionContext context,
2214
                        final MappingColumn[] mappingColumns,
2215
                        final TableMetadata metadata,
2216
                        final E entity) {
2217
                var autoGeneratedColumns = new ArrayList<MappingColumn>();
1✔
2218
                var autoGeneratedColumnNames = new ArrayList<String>();
1✔
2219

2220
                if (mappingColumns != null && mappingColumns.length > 0) {
1✔
2221
                        for (var column : metadata.getColumns()) {
1✔
2222
                                if (column.isAutoincrement()) {
1✔
2223
                                        for (var mappingColumn : mappingColumns) {
1✔
2224
                                                if (mappingColumn.getCamelName().equals(column.getCamelColumnName())) {
1✔
2225
                                                        // primitive型か、エンティティの対象フィールドがnullとなっている場合自動生成カラムとする
2226
                                                        if (mappingColumn.getJavaType().getRawType().isPrimitive()
1✔
2227
                                                                        || mappingColumn.getValue(entity) == null) {
1✔
2228
                                                                autoGeneratedColumns.add(mappingColumn);
1✔
2229
                                                                autoGeneratedColumnNames.add(column.getColumnName());
1✔
2230
                                                        }
2231
                                                        break;
2232
                                                }
2233
                                        }
2234
                                }
2235
                        }
1✔
2236
                        for (var mappingColumn : mappingColumns) {
1✔
2237
                                if (!mappingColumn.isId() || autoGeneratedColumns.contains(mappingColumn)) {
1✔
2238
                                        continue;
1✔
2239
                                }
2240
                                for (var column : metadata.getColumns()) {
1✔
2241
                                        if (mappingColumn.getCamelName().equals(column.getCamelColumnName())) {
1✔
2242
                                                // primitive型か、エンティティの対象フィールドがnullとなっている場合自動生成カラムとする
2243
                                                if (mappingColumn.getJavaType().getRawType().isPrimitive()
1✔
2244
                                                                || mappingColumn.getValue(entity) == null) {
1✔
2245
                                                        autoGeneratedColumns.add(mappingColumn);
1✔
2246
                                                        autoGeneratedColumnNames.add(column.getColumnName());
1✔
2247
                                                }
2248
                                                break;
2249
                                        }
2250
                                }
1✔
2251
                        }
2252
                }
2253

2254
                if (!autoGeneratedColumnNames.isEmpty()) {
1✔
2255
                        context.setGeneratedKeyColumns(
1✔
2256
                                        autoGeneratedColumnNames.toArray(new String[autoGeneratedColumnNames.size()]));
1✔
2257
                }
2258
                return autoGeneratedColumns;
1✔
2259
        }
2260

2261
        /**
2262
         * entityにID値を設定する
2263
         * @param entity Entity
2264
         * @param id ID値
2265
         * @param column 設定する対象のカラム
2266
         */
2267
        private void setEntityIdValue(final Object entity, final Object id, final MappingColumn column) {
2268
                var rawType = column.getJavaType().getRawType();
1✔
2269
                try {
2270
                        if (int.class.equals(rawType) || Integer.class.equals(rawType)) {
1✔
2271
                                if (id instanceof Number) {
1✔
2272
                                        column.setValue(entity, ((Number) id).intValue());
1✔
2273
                                } else {
2274
                                        throw new IllegalArgumentException();
1✔
2275
                                }
2276
                        } else if (long.class.equals(rawType) || Long.class.equals(rawType)) {
1✔
2277
                                if (id instanceof Number) {
1✔
2278
                                        column.setValue(entity, ((Number) id).longValue());
1✔
2279
                                } else {
2280
                                        throw new IllegalArgumentException();
1✔
2281
                                }
2282
                        } else if (BigInteger.class.equals(rawType)) {
1✔
2283
                                if (id instanceof BigInteger) {
1✔
2284
                                        column.setValue(entity, id);
×
2285
                                } else if (id instanceof BigDecimal) {
1✔
2286
                                        column.setValue(entity, ((BigDecimal) id).toBigInteger());
1✔
2287
                                } else if (id instanceof Number) {
1✔
2288
                                        column.setValue(entity, new BigInteger(((Number) id).toString()));
1✔
2289
                                } else {
2290
                                        throw new IllegalArgumentException();
1✔
2291
                                }
2292
                        } else if (BigDecimal.class.equals(rawType)) {
1✔
2293
                                if (id instanceof BigDecimal) {
1✔
2294
                                        column.setValue(entity, id);
1✔
2295
                                } else if (id instanceof BigInteger) {
1✔
2296
                                        column.setValue(entity, new BigDecimal((BigInteger) id));
×
2297
                                } else if (id instanceof Number) {
1✔
2298
                                        column.setValue(entity, new BigDecimal(((Number) id).toString()));
1✔
2299
                                } else {
2300
                                        throw new IllegalArgumentException();
1✔
2301
                                }
2302
                        } else if (String.class.equals(rawType)) {
1✔
2303
                                if (id instanceof String) {
1✔
2304
                                        column.setValue(entity, id);
×
2305
                                } else if (id instanceof BigDecimal) {
1✔
2306
                                        column.setValue(entity, ((BigDecimal) id).toPlainString());
1✔
2307
                                } else if (id instanceof Number) {
1✔
2308
                                        column.setValue(entity, ((Number) id).toString());
1✔
2309
                                } else {
2310
                                        column.setValue(entity, id.toString());
1✔
2311
                                }
2312
                        } else {
2313
                                try {
2314
                                        column.setValue(entity, id);
1✔
2315
                                } catch (UroborosqlRuntimeException ex) {
1✔
2316
                                        throw new UroborosqlRuntimeException(
1✔
2317
                                                        "Column is not correct as ID type. column=" + column.getCamelName() + ", type=" + rawType);
1✔
2318
                                }
1✔
2319
                        }
2320
                } catch (IllegalArgumentException ex) {
1✔
2321
                        throw new UroborosqlRuntimeException(
1✔
2322
                                        "Column type and ID type do not match. column=" + column.getCamelName() + ", type="
1✔
2323
                                                        + rawType
2324
                                                        + ", id=" + id + ", type=" + id.getClass().getSimpleName());
1✔
2325
                }
1✔
2326
        }
1✔
2327

2328
        /**
2329
         *
2330
         * {@inheritDoc}
2331
         *
2332
         * @see jp.co.future.uroborosql.SqlAgent#getSqlConfig()
2333
         */
2334
        @Override
2335
        public SqlConfig getSqlConfig() {
2336
                return this.sqlConfig;
1✔
2337
        }
2338

2339
        /**
2340
         *
2341
         * {@inheritDoc}
2342
         *
2343
         * @see jp.co.future.uroborosql.SqlAgent#context()
2344
         */
2345
        @Override
2346
        public ExecutionContext context() {
2347
                return sqlConfig.context();
1✔
2348
        }
2349

2350
        /**
2351
         *
2352
         * {@inheritDoc}
2353
         *
2354
         * @see jp.co.future.uroborosql.SqlAgent#contextFrom(java.lang.String)
2355
         */
2356
        @Deprecated
2357
        @Override
2358
        public ExecutionContext contextFrom(final String sqlName) {
2359
                return sqlConfig.context().setSqlName(sqlName);
×
2360
        }
2361

2362
        /**
2363
         *
2364
         * {@inheritDoc}
2365
         *
2366
         * @see jp.co.future.uroborosql.SqlAgent#contextWith(java.lang.String)
2367
         */
2368
        @Deprecated
2369
        @Override
2370
        public ExecutionContext contextWith(final String sql) {
2371
                return sqlConfig.context().setSql(sql);
×
2372
        }
2373

2374
        /**
2375
         * {@inheritDoc}
2376
         *
2377
         * @see jp.co.future.uroborosql.SqlAgent#getFetchSize()
2378
         */
2379
        @Override
2380
        public int getFetchSize() {
2381
                return fetchSize;
1✔
2382
        }
2383

2384
        /**
2385
         * {@inheritDoc}
2386
         *
2387
         * @see jp.co.future.uroborosql.SqlAgent#setFetchSize(int)
2388
         */
2389
        @Override
2390
        public void setFetchSize(final int fetchSize) {
2391
                this.fetchSize = fetchSize;
×
2392
        }
×
2393

2394
        /**
2395
         * {@inheritDoc}
2396
         *
2397
         * @see jp.co.future.uroborosql.SqlAgent#getQueryTimeout()
2398
         */
2399
        @Override
2400
        public int getQueryTimeout() {
2401
                return queryTimeout;
1✔
2402
        }
2403

2404
        /**
2405
         * {@inheritDoc}
2406
         *
2407
         * @see jp.co.future.uroborosql.SqlAgent#setQueryTimeout(int)
2408
         */
2409
        @Override
2410
        public void setQueryTimeout(final int queryTimeout) {
2411
                this.queryTimeout = queryTimeout;
×
2412
        }
×
2413

2414
        /**
2415
         * SQL実行をリトライするSQLエラーコードのリスト を取得します
2416
         *
2417
         * @return SQL実行をリトライするSQLエラーコードのリスト
2418
         */
2419
        public List<String> getSqlRetryCodes() {
2420
                return sqlRetryCodes;
1✔
2421
        }
2422

2423
        /**
2424
         * SQL実行をリトライするSQLエラーコードのリスト を設定します
2425
         *
2426
         * @param sqlRetryCodes SQL実行をリトライするSQLエラーコードのリスト
2427
         */
2428
        public void setSqlRetryCodes(final List<String> sqlRetryCodes) {
2429
                this.sqlRetryCodes = sqlRetryCodes;
×
2430
        }
×
2431

2432
        /**
2433
         * 最大リトライ回数 を取得します
2434
         *
2435
         * @return 最大リトライ回数
2436
         */
2437
        public int getMaxRetryCount() {
2438
                return maxRetryCount;
1✔
2439
        }
2440

2441
        /**
2442
         * 最大リトライ回数 を設定します
2443
         *
2444
         * @param maxRetryCount 最大リトライ回数
2445
         */
2446
        public void setMaxRetryCount(final int maxRetryCount) {
2447
                this.maxRetryCount = maxRetryCount;
×
2448
        }
×
2449

2450
        /**
2451
         * リトライタイムアウト時間(ms) を取得します
2452
         *
2453
         * @return リトライタイムアウト時間(ms)
2454
         */
2455
        public int getRetryWaitTime() {
2456
                return retryWaitTime;
1✔
2457
        }
2458

2459
        /**
2460
         * リトライタイムアウト時間(ms) を設定します
2461
         *
2462
         * @param retryWaitTime リトライタイムアウト時間(ms)
2463
         */
2464
        public void setRetryWaitTime(final int retryWaitTime) {
2465
                this.retryWaitTime = retryWaitTime;
×
2466
        }
×
2467

2468
        /**
2469
         *
2470
         * {@inheritDoc}
2471
         *
2472
         * @see jp.co.future.uroborosql.SqlAgent#getMapKeyCaseFormat()
2473
         */
2474
        @Override
2475
        public CaseFormat getMapKeyCaseFormat() {
2476
                return mapKeyCaseFormat;
1✔
2477
        }
2478

2479
        /**
2480
         *
2481
         * {@inheritDoc}
2482
         *
2483
         * @see jp.co.future.uroborosql.SqlAgent#setMapKeyCaseFormat(jp.co.future.uroborosql.utils.CaseFormat)
2484
         */
2485
        @Override
2486
        public void setMapKeyCaseFormat(final CaseFormat mapKeyCaseFormat) {
2487
                this.mapKeyCaseFormat = mapKeyCaseFormat;
×
2488
        }
×
2489

2490
        /**
2491
         *
2492
         * {@inheritDoc}
2493
         *
2494
         * @see jp.co.future.uroborosql.SqlAgent#getInsertsType()
2495
         */
2496
        @Override
2497
        public InsertsType getInsertsType() {
2498
                return this.insertsType;
1✔
2499
        }
2500

2501
        /**
2502
         *
2503
         * {@inheritDoc}
2504
         *
2505
         * @see jp.co.future.uroborosql.SqlAgent#setInsertsType(jp.co.future.uroborosql.enums.InsertsType)
2506
         */
2507
        @Override
2508
        public void setInsertsType(final InsertsType defaultInsertsType) {
2509
                this.insertsType = defaultInsertsType;
1✔
2510
        }
1✔
2511

2512
        /**
2513
         * SQLリソース管理クラスを取得します。
2514
         *
2515
         * @return SQLリソース管理クラス
2516
         */
2517
        private SqlResourceManager getSqlResourceManager() {
2518
                return sqlConfig.getSqlResourceManager();
1✔
2519
        }
2520

2521
        /**
2522
         * ORM処理クラス を取得します。
2523
         *
2524
         * @return ORM処理クラス
2525
         */
2526
        @SuppressWarnings("unchecked")
2527
        private <E> EntityHandler<E> getEntityHandler() {
2528
                return (EntityHandler<E>) sqlConfig.getEntityHandler();
1✔
2529
        }
2530

2531
        /**
2532
         * Dialect を取得します。
2533
         *
2534
         * @return Dialect
2535
         */
2536
        private Dialect getDialect() {
2537
                return sqlConfig.getDialect();
1✔
2538
        }
2539

2540
        /**
2541
         * ステートメント初期化。
2542
         *
2543
         * @param executionContext ExecutionContext
2544
         * @return PreparedStatement
2545
         * @throws SQLException SQL例外
2546
         */
2547
        private PreparedStatement getPreparedStatement(final ExecutionContext executionContext) throws SQLException {
2548
                var stmt = ((LocalTransactionManager) transactionManager).getPreparedStatement(executionContext);
1✔
2549
                // プロパティ設定
2550
                applyProperties(stmt);
1✔
2551
                return stmt;
1✔
2552
        }
2553

2554
        /**
2555
         * Callableステートメント初期化
2556
         *
2557
         * @param executionContext ExecutionContext
2558
         * @return CallableStatement
2559
         * @throws SQLException SQL例外
2560
         */
2561
        private CallableStatement getCallableStatement(final ExecutionContext executionContext) throws SQLException {
2562
                var stmt = ((LocalTransactionManager) transactionManager).getCallableStatement(executionContext);
1✔
2563
                // プロパティ設定
2564
                applyProperties(stmt);
1✔
2565
                return stmt;
1✔
2566
        }
2567

2568
        /**
2569
         * フェッチサイズとクエリタイムアウトをPreparedStatementに設定する
2570
         *
2571
         * @param preparedStatement PreparedStatement
2572
         * @throws SQLException SQL例外
2573
         */
2574
        private void applyProperties(final PreparedStatement preparedStatement) throws SQLException {
2575
                // フェッチサイズ指定
2576
                if (getFetchSize() >= 0 && !(preparedStatement instanceof CallableStatement)) {
1✔
2577
                        preparedStatement.setFetchSize(getFetchSize());
1✔
2578
                }
2579

2580
                // クエリタイムアウト指定
2581
                if (getQueryTimeout() >= 0) {
1✔
2582
                        preparedStatement.setQueryTimeout(getQueryTimeout());
1✔
2583
                }
2584
        }
1✔
2585

2586
        /**
2587
         * 経過時間を計算し、HH:mm:ss.SSSSSSにフォーマットする.
2588
         *
2589
         * @param start 開始時間
2590
         * @param end 終了時間
2591
         * @return フォーマットした経過時間
2592
         */
2593
        private static String formatElapsedTime(final Instant start, final Instant end) {
2594
                return ELAPSED_TIME_FORMAT.format(LocalTime.MIDNIGHT.plus(Duration.between(start, end)));
1✔
2595
        }
2596

2597
        /**
2598
         * ResultSetをStreamで扱うためのSpliterator
2599
         *
2600
         * @author H.Sugimoto
2601
         *
2602
         * @param <T> ResultSetの1行を変換した型
2603
         */
2604
        private static final class ResultSetSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
2605
                private final ResultSetConverter<T> converter;
2606
                private final ResultSet rs;
2607
                private boolean finished = false;
1✔
2608

2609
                private ResultSetSpliterator(final ResultSet rs, final ResultSetConverter<T> converter) {
2610
                        super(Long.MAX_VALUE, Spliterator.ORDERED);
1✔
2611
                        this.rs = rs;
1✔
2612
                        this.converter = converter;
1✔
2613
                }
1✔
2614

2615
                @Override
2616
                public boolean tryAdvance(final Consumer<? super T> action) {
2617
                        try {
2618
                                if (finished || !rs.next()) {
1✔
2619
                                        if (!rs.isClosed()) {
1✔
2620
                                                rs.close();
1✔
2621
                                        }
2622
                                        finished = true;
1✔
2623
                                        return false;
1✔
2624
                                }
2625
                                action.accept(converter.createRecord(rs));
1✔
2626
                                return true;
1✔
2627
                        } catch (RuntimeException | Error ex) {
1✔
2628
                                try {
2629
                                        if (rs != null && !rs.isClosed()) {
1✔
2630
                                                rs.close();
1✔
2631
                                        }
2632
                                } catch (SQLException e) {
×
2633
                                        e.printStackTrace();
×
2634
                                }
1✔
2635
                                throw ex;
1✔
2636
                        } catch (SQLException ex) {
×
2637
                                try {
2638
                                        if (rs != null && !rs.isClosed()) {
×
2639
                                                rs.close();
×
2640
                                        }
2641
                                } catch (SQLException e) {
×
2642
                                        e.printStackTrace();
×
2643
                                }
×
2644
                                throw new UroborosqlSQLException(ex);
×
2645
                        }
2646
                }
2647
        }
2648

2649
        /**
2650
         * ResultSetのラッパークラス。ResultSetのクローズに合わせてStatementもクローズする。
2651
         *
2652
         * @author H.Sugimoto
2653
         * @version 0.5.0
2654
         */
2655
        private static class InnerResultSet extends AbstractResultSetWrapper {
2656
                /** 同期してクローズするStatement */
2657
                private final Statement stmt;
2658

2659
                /**
2660
                 * コンストラクタ
2661
                 *
2662
                 * @param wrapped 元となるResultSet
2663
                 * @param stmt Statement
2664
                 */
2665
                InnerResultSet(final ResultSet wrapped, final Statement stmt) {
2666
                        super(wrapped);
1✔
2667
                        this.stmt = stmt;
1✔
2668
                }
1✔
2669

2670
                /**
2671
                 * {@inheritDoc}
2672
                 *
2673
                 * @see jp.co.future.uroborosql.AbstractResultSetWrapper#close()
2674
                 */
2675
                @Override
2676
                public void close() throws SQLException {
2677
                        try {
2678
                                super.close();
1✔
2679
                        } finally {
2680
                                try {
2681
                                        if (stmt != null && !stmt.isClosed()) {
1✔
2682
                                                stmt.close();
1✔
2683
                                        }
2684
                                } catch (SQLException e) {
×
2685
                                        // do nothing
2686
                                }
1✔
2687
                        }
2688
                }
1✔
2689
        }
2690
}
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