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

future-architect / uroborosql / #832

03 May 2025 06:41AM UTC coverage: 89.859% (-0.06%) from 89.914%
#832

Pull #350

HidekiSugimoto189
add matrix test and dialect flag.
Pull Request #350: Add multi sqlfile bulk update method.

14 of 16 new or added lines in 3 files covered. (87.5%)

5 existing lines in 1 file now uncovered.

8701 of 9683 relevant lines covered (89.86%)

0.9 hits per line

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

88.99
/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 jp.co.future.uroborosql.config.SqlConfig;
40
import jp.co.future.uroborosql.connection.ConnectionContext;
41
import jp.co.future.uroborosql.context.ExecutionContext;
42
import jp.co.future.uroborosql.context.ExecutionContextImpl;
43
import jp.co.future.uroborosql.converter.MapResultSetConverter;
44
import jp.co.future.uroborosql.converter.ResultSetConverter;
45
import jp.co.future.uroborosql.coverage.CoverageData;
46
import jp.co.future.uroborosql.coverage.CoverageHandler;
47
import jp.co.future.uroborosql.dialect.Dialect;
48
import jp.co.future.uroborosql.enums.InsertsType;
49
import jp.co.future.uroborosql.enums.SqlKind;
50
import jp.co.future.uroborosql.event.AfterEntityBatchInsertEvent;
51
import jp.co.future.uroborosql.event.AfterEntityBatchUpdateEvent;
52
import jp.co.future.uroborosql.event.AfterEntityBulkInsertEvent;
53
import jp.co.future.uroborosql.event.AfterEntityDeleteEvent;
54
import jp.co.future.uroborosql.event.AfterEntityInsertEvent;
55
import jp.co.future.uroborosql.event.AfterEntityQueryEvent;
56
import jp.co.future.uroborosql.event.AfterEntityUpdateEvent;
57
import jp.co.future.uroborosql.event.AfterProcedureEvent;
58
import jp.co.future.uroborosql.event.AfterSqlBatchEvent;
59
import jp.co.future.uroborosql.event.AfterSqlQueryEvent;
60
import jp.co.future.uroborosql.event.AfterSqlUpdateEvent;
61
import jp.co.future.uroborosql.event.BeforeEntityBatchInsertEvent;
62
import jp.co.future.uroborosql.event.BeforeEntityBatchUpdateEvent;
63
import jp.co.future.uroborosql.event.BeforeEntityBulkInsertEvent;
64
import jp.co.future.uroborosql.event.BeforeEntityDeleteEvent;
65
import jp.co.future.uroborosql.event.BeforeEntityInsertEvent;
66
import jp.co.future.uroborosql.event.BeforeEntityQueryEvent;
67
import jp.co.future.uroborosql.event.BeforeEntityUpdateEvent;
68
import jp.co.future.uroborosql.event.BeforeParseSqlEvent;
69
import jp.co.future.uroborosql.event.BeforeTransformSqlEvent;
70
import jp.co.future.uroborosql.exception.EntitySqlRuntimeException;
71
import jp.co.future.uroborosql.exception.OptimisticLockException;
72
import jp.co.future.uroborosql.exception.PessimisticLockException;
73
import jp.co.future.uroborosql.exception.UroborosqlRuntimeException;
74
import jp.co.future.uroborosql.exception.UroborosqlSQLException;
75
import jp.co.future.uroborosql.fluent.Procedure;
76
import jp.co.future.uroborosql.fluent.SqlBatch;
77
import jp.co.future.uroborosql.fluent.SqlEntityDelete;
78
import jp.co.future.uroborosql.fluent.SqlEntityQuery;
79
import jp.co.future.uroborosql.fluent.SqlEntityUpdate;
80
import jp.co.future.uroborosql.fluent.SqlQuery;
81
import jp.co.future.uroborosql.fluent.SqlUpdate;
82
import jp.co.future.uroborosql.log.support.CoverageLoggingSupport;
83
import jp.co.future.uroborosql.log.support.PerformanceLoggingSupport;
84
import jp.co.future.uroborosql.log.support.ServiceLoggingSupport;
85
import jp.co.future.uroborosql.log.support.SqlLoggingSupport;
86
import jp.co.future.uroborosql.mapping.EntityHandler;
87
import jp.co.future.uroborosql.mapping.MappingColumn;
88
import jp.co.future.uroborosql.mapping.MappingUtils;
89
import jp.co.future.uroborosql.mapping.TableMetadata;
90
import jp.co.future.uroborosql.parser.SqlParserImpl;
91
import jp.co.future.uroborosql.store.SqlResourceManager;
92
import jp.co.future.uroborosql.tx.LocalTransactionManager;
93
import jp.co.future.uroborosql.tx.TransactionManager;
94
import jp.co.future.uroborosql.utils.CaseFormat;
95
import jp.co.future.uroborosql.utils.ObjectUtils;
96

97
/**
98
 * SQL実行用クラス。
99
 *
100
 * @author H.Sugimoto
101
 */
102
public class SqlAgentImpl implements SqlAgent, ServiceLoggingSupport, PerformanceLoggingSupport, SqlLoggingSupport,
103
                CoverageLoggingSupport {
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> COVERAGE_HANDLER_REF = 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.atDebug()
×
161
                                        .log("system property - uroborosql.sql.coverage not set. sql coverage turned off.");
×
162
                } else if (Boolean.FALSE.toString().equalsIgnoreCase(sqlCoverageClassName)) {
1✔
163
                        sqlCoverageClassName = null;
×
164
                        COVERAGE_LOG.atDebug()
×
165
                                        .log("system property - uroborosql.sql.coverage is set to false. sql coverage turned off.");
×
166
                } else if (Boolean.TRUE.toString().equalsIgnoreCase(sqlCoverageClassName)) {
1✔
167
                        // trueの場合は、デフォルト値を設定
168
                        sqlCoverageClassName = "jp.co.future.uroborosql.coverage.CoberturaCoverageHandler";
1✔
169
                        COVERAGE_LOG.atDebug()
1✔
170
                                        .log("system property - uroborosql.sql.coverage is set to true. sql coverage turned on.");
1✔
171
                } else {
172
                        COVERAGE_LOG.atDebug()
×
173
                                        .setMessage(
×
174
                                                        "system property - uroborosql.sql.coverage is set to custom class:{}. sql coverage turned on.")
175
                                        .addArgument(sqlCoverageClassName)
×
176
                                        .log();
×
177
                }
178

179
                CoverageHandler handler = null;
1✔
180
                if (sqlCoverageClassName != null) {
1✔
181
                        try {
182
                                handler = (CoverageHandler) Class.forName(sqlCoverageClassName, true,
1✔
183
                                                Thread.currentThread().getContextClassLoader()).getConstructor().newInstance();
1✔
184
                                COVERAGE_LOG.atDebug()
1✔
185
                                                .setMessage("CoverageHandler : {}")
1✔
186
                                                .addArgument(sqlCoverageClassName)
1✔
187
                                                .log();
1✔
188
                        } catch (Exception ex) {
×
189
                                COVERAGE_LOG.atWarn()
×
190
                                                .setMessage("Failed to instantiate CoverageHandler class. Class:{}, Cause:{}")
×
191
                                                .addArgument(sqlCoverageClassName)
×
192
                                                .addArgument(ex.getMessage())
×
193
                                                .log();
×
194
                                COVERAGE_LOG.atDebug()
×
195
                                                .log("Turn off sql coverage due to failure to instantiate CoverageHandler class.");
×
196
                        }
1✔
197
                }
198

199
                if (handler != null) {
1✔
200
                        COVERAGE_HANDLER_REF.set(handler);
1✔
201
                }
202
        }
1✔
203

204
        /**
205
         * コンストラクタ。
206
         *
207
         * @param sqlConfig SQL設定管理クラス
208
         * @param settings 設定情報
209
         * @param connectionContext DB接続情報
210
         */
211
        SqlAgentImpl(final SqlConfig sqlConfig, final Map<String, String> settings,
212
                        final ConnectionContext connectionContext) {
1✔
213
                this.sqlConfig = sqlConfig;
1✔
214
                this.transactionManager = new LocalTransactionManager(sqlConfig, connectionContext);
1✔
215

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

255
        /**
256
         * {@inheritDoc}
257
         *
258
         * @see jp.co.future.uroborosql.SqlAgent#close()
259
         */
260
        @Override
261
        public void close() {
262
                transactionManager.close();
1✔
263
                if (COVERAGE_HANDLER_REF.get() != null) {
1✔
264
                        COVERAGE_HANDLER_REF.get().onSqlAgentClose();
1✔
265
                }
266
                releaseLogging();
1✔
267
        }
1✔
268

269
        /**
270
         *
271
         * {@inheritDoc}
272
         *
273
         * @see jp.co.future.uroborosql.connection.ConnectionManager#getConnection()
274
         */
275
        @Override
276
        public Connection getConnection() {
277
                return transactionManager.getConnection();
1✔
278
        }
279

280
        /**
281
         *
282
         * {@inheritDoc}
283
         *
284
         * @see jp.co.future.uroborosql.tx.TransactionManager#required(java.lang.Runnable)
285
         */
286
        @Override
287
        public void required(final Runnable runnable) {
288
                transactionManager.required(runnable);
1✔
289
        }
1✔
290

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

302
        /**
303
         * {@inheritDoc}
304
         *
305
         * @see jp.co.future.uroborosql.tx.TransactionManager#requiresNew(jp.co.future.uroborosql.tx.Runnable)
306
         */
307
        @Override
308
        public void requiresNew(final Runnable runnable) {
309
                transactionManager.requiresNew(runnable);
1✔
310
        }
1✔
311

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

323
        /**
324
         *
325
         * {@inheritDoc}
326
         *
327
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.lang.Runnable)
328
         */
329
        @Override
330
        public void notSupported(final Runnable runnable) {
331
                transactionManager.notSupported(runnable);
1✔
332
        }
1✔
333

334
        /**
335
         *
336
         * {@inheritDoc}
337
         *
338
         * @see jp.co.future.uroborosql.tx.TransactionManager#notSupported(java.util.function.Supplier)
339
         */
340
        @Override
341
        public <R> R notSupported(final Supplier<R> supplier) {
342
                return transactionManager.notSupported(supplier);
1✔
343
        }
344

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

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

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

378
        /**
379
         *
380
         * {@inheritDoc}
381
         *
382
         * @see jp.co.future.uroborosql.tx.TransactionManager#rollback(java.lang.String)
383
         */
384
        @Override
385
        public void rollback(final String savepointName) {
386
                transactionManager.rollback(savepointName);
1✔
387
        }
1✔
388

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

400
        /**
401
         *
402
         * {@inheritDoc}
403
         *
404
         * @see jp.co.future.uroborosql.tx.TransactionManager#savepointScope(java.lang.Runnable)
405
         */
406
        @Override
407
        public void savepointScope(final Runnable runnable) {
408
                transactionManager.savepointScope(runnable);
1✔
409
        }
1✔
410

411
        /**
412
         *
413
         * {@inheritDoc}
414
         *
415
         * @see jp.co.future.uroborosql.tx.TransactionManager#autoCommitScope(java.util.function.Supplier)
416
         */
417
        @Override
418
        public <R> R autoCommitScope(final Supplier<R> supplier) {
419
                return transactionManager.autoCommitScope(supplier);
1✔
420
        }
421

422
        /**
423
         *
424
         * {@inheritDoc}
425
         *
426
         * @see jp.co.future.uroborosql.tx.TransactionManager#autoCommitScope(java.lang.Runnable)
427
         */
428
        @Override
429
        public void autoCommitScope(final Runnable runnable) {
430
                transactionManager.autoCommitScope(runnable);
1✔
431
        }
1✔
432

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

444
        /**
445
         *
446
         * {@inheritDoc}
447
         *
448
         * @see jp.co.future.uroborosql.connection.ConnectionManager#rollback()
449
         */
450
        @Override
451
        public void rollback() {
452
                transactionManager.rollback();
1✔
453
        }
1✔
454

455
        /**
456
         * {@inheritDoc}
457
         *
458
         * @see jp.co.future.uroborosql.SqlAgent#query(java.lang.String)
459
         */
460
        @Override
461
        public SqlQuery query(final String sqlName) {
462
                if ("".equals(sqlName)) {
1✔
463
                        throw new IllegalArgumentException("sqlName is required.");
1✔
464
                }
465
                return new SqlQueryImpl(this, context().setSqlName(sqlName));
1✔
466
        }
467

468
        /**
469
         * {@inheritDoc}
470
         *
471
         * @see jp.co.future.uroborosql.SqlAgent#query(java.util.function.Supplier)
472
         */
473
        @Override
474
        public SqlQuery query(final Supplier<String> supplier) {
475
                return this.query(supplier.get());
1✔
476
        }
477

478
        /**
479
         * {@inheritDoc}
480
         *
481
         * @see jp.co.future.uroborosql.SqlAgent#queryWith(java.lang.String)
482
         */
483
        @Override
484
        public SqlQuery queryWith(final String sql) {
485
                if (sql == null || "".equals(sql)) {
1✔
486
                        throw new IllegalArgumentException("sql is required.");
1✔
487
                }
488
                return new SqlQueryImpl(this, context().setSql(sql));
1✔
489
        }
490

491
        /**
492
         * {@inheritDoc}
493
         *
494
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.String)
495
         */
496
        @Override
497
        public SqlUpdate update(final String sqlName) {
498
                if ("".equals(sqlName)) {
1✔
499
                        throw new IllegalArgumentException("sqlName is required.");
1✔
500
                }
501
                return new SqlUpdateImpl(this, context().setSqlName(sqlName));
1✔
502
        }
503

504
        /**
505
         * {@inheritDoc}
506
         *
507
         * @see jp.co.future.uroborosql.SqlAgent#update(java.util.function.Supplier)
508
         */
509
        @Override
510
        public SqlUpdate update(final Supplier<String> supplier) {
511
                return this.update(supplier.get());
1✔
512
        }
513

514
        /**
515
         * {@inheritDoc}
516
         *
517
         * @see jp.co.future.uroborosql.SqlAgent#updateWith(java.lang.String)
518
         */
519
        @Override
520
        public SqlUpdate updateWith(final String sql) {
521
                if (sql == null || "".equals(sql)) {
1✔
522
                        throw new IllegalArgumentException("sql is required.");
1✔
523
                }
524
                return new SqlUpdateImpl(this, context().setSql(sql));
1✔
525
        }
526

527
        /**
528
         * {@inheritDoc}
529
         *
530
         * @see jp.co.future.uroborosql.SqlAgent#updateChained(java.util.List)
531
         */
532
        @Override
533
        public SqlUpdate updateChained(final List<String> sqlNames) {
534
                if (sqlNames == null || sqlNames.isEmpty()) {
1✔
535
                        throw new IllegalArgumentException("sqlNames is required.");
1✔
536
                }
537
                if (sqlNames.size() == 1) {
1✔
538
                        return update(sqlNames.get(0));
1✔
539
                }
540
                var sqls = sqlNames.stream()
1✔
541
                                .map(sqlName -> {
1✔
542
                                        var sql = getSqlResourceManager().getSql(sqlName);
1✔
543
                                        if (ObjectUtils.isEmpty(sql)) {
1✔
NEW
544
                                                throw new UroborosqlRuntimeException("sql file:[" + sqlName + "] is not found.");
×
545
                                        }
546
                                        return sql;
1✔
547
                                })
548
                                .collect(Collectors.joining(";" + System.lineSeparator()));
1✔
549

550
                var sqlName = sqlNames.stream()
1✔
551
                                .collect(Collectors.joining(","));
1✔
552

553
                return new SqlUpdateImpl(this, context().setSqlName(sqlName).setSql(sqls));
1✔
554
        }
555

556
        /**
557
         * {@inheritDoc}
558
         *
559
         * @see jp.co.future.uroborosql.SqlAgent#batch(java.lang.String)
560
         */
561
        @Override
562
        public SqlBatch batch(final String sqlName) {
563
                if ("".equals(sqlName)) {
1✔
564
                        throw new IllegalArgumentException("sqlName is required.");
1✔
565
                }
566
                return new SqlBatchImpl(this, context().setSqlName(sqlName));
1✔
567
        }
568

569
        /**
570
         * {@inheritDoc}
571
         *
572
         * @see jp.co.future.uroborosql.SqlAgent#batch(java.util.function.Supplier)
573
         */
574
        @Override
575
        public SqlBatch batch(final Supplier<String> supplier) {
576
                return this.batch(supplier.get());
1✔
577
        }
578

579
        /**
580
         * {@inheritDoc}
581
         *
582
         * @see jp.co.future.uroborosql.SqlAgent#batchWith(java.lang.String)
583
         */
584
        @Override
585
        public SqlBatch batchWith(final String sql) {
586
                if (sql == null || "".equals(sql)) {
1✔
587
                        throw new IllegalArgumentException("sql is required.");
1✔
588
                }
589
                return new SqlBatchImpl(this, context().setSql(sql));
1✔
590
        }
591

592
        /**
593
         * {@inheritDoc}
594
         *
595
         * @see jp.co.future.uroborosql.SqlAgent#proc(java.lang.String)
596
         */
597
        @Override
598
        public Procedure proc(final String sqlName) {
599
                if ("".equals(sqlName)) {
1✔
600
                        throw new IllegalArgumentException("sqlName is required.");
1✔
601
                }
602
                return new ProcedureImpl(this, context().setSqlName(sqlName));
1✔
603
        }
604

605
        /**
606
         * {@inheritDoc}
607
         *
608
         * @see jp.co.future.uroborosql.SqlAgent#proc(java.util.function.Supplier)
609
         */
610
        @Override
611
        public Procedure proc(final Supplier<String> supplier) {
612
                return this.proc(supplier.get());
1✔
613
        }
614

615
        /**
616
         * {@inheritDoc}
617
         *
618
         * @see jp.co.future.uroborosql.SqlAgent#procWith(java.lang.String)
619
         */
620
        @Override
621
        public Procedure procWith(final String sql) {
622
                if (sql == null || "".equals(sql)) {
1✔
623
                        throw new IllegalArgumentException("sql is required.");
1✔
624
                }
625
                return new ProcedureImpl(this, context().setSql(sql));
1✔
626
        }
627

628
        /**
629
         * {@inheritDoc}
630
         *
631
         * @see jp.co.future.uroborosql.SqlAgent#query(java.lang.Class)
632
         */
633
        @Override
634
        public <E> SqlEntityQuery<E> query(final Class<? extends E> entityType) {
635
                var handler = this.getEntityHandler();
1✔
636
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
637
                        throw new IllegalArgumentException("Entity type not supported");
×
638
                }
639
                try {
640
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
641
                        var context = handler.createSelectContext(this, metadata, entityType, false);
1✔
642
                        context.setSqlKind(SqlKind.ENTITY_SELECT);
1✔
643
                        return new SqlEntityQueryImpl<>(this, handler, metadata, context, entityType);
1✔
644
                } catch (SQLException ex) {
×
645
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_SELECT, ex);
×
646
                }
647
        }
648

649
        /**
650
         * {@inheritDoc}
651
         *
652
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
653
         */
654
        @Override
655
        public <E> int inserts(final Class<E> entityType,
656
                        final Stream<E> entities,
657
                        final InsertsCondition<? super E> condition,
658
                        final InsertsType insertsType) {
659
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
660
                        return bulkInsert(entityType, entities, condition, null);
1✔
661
                } else {
662
                        return batchInsert(entityType, entities, condition, null);
1✔
663
                }
664
        }
665

666
        /**
667
         * {@inheritDoc}
668
         *
669
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream)
670
         */
671
        @Override
672
        public <E> int inserts(final Class<E> entityType,
673
                        final Stream<E> entities) {
674
                return inserts(entityType, entities, getInsertsCondition(getInsertsType()));
1✔
675
        }
676

677
        /**
678
         * {@inheritDoc}
679
         *
680
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
681
         */
682
        @Override
683
        public <E> int inserts(final Class<E> entityType,
684
                        final Stream<E> entities,
685
                        final InsertsCondition<? super E> condition) {
686
                return inserts(entityType, entities, condition, getInsertsType());
1✔
687
        }
688

689
        /**
690
         * {@inheritDoc}
691
         *
692
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
693
         */
694
        @Override
695
        public <E> int inserts(final Class<E> entityType,
696
                        final Stream<E> entities,
697
                        final InsertsType insertsType) {
698
                return inserts(entityType, entities, getInsertsCondition(insertsType), insertsType);
1✔
699
        }
700

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

722
        /**
723
         * {@inheritDoc}
724
         *
725
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream)
726
         */
727
        @Override
728
        public <E> int inserts(final Stream<E> entities) {
729
                return inserts(entities, getInsertsCondition(getInsertsType()));
1✔
730
        }
731

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

743
        /**
744
         * {@inheritDoc}
745
         *
746
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
747
         */
748
        @Override
749
        public <E> int inserts(final Stream<E> entities,
750
                        final InsertsType insertsType) {
751
                return inserts(entities, getInsertsCondition(insertsType), insertsType);
1✔
752
        }
753

754
        /**
755
         * {@inheritDoc}
756
         *
757
         * @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)
758
         */
759
        @Override
760
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
761
                        final Stream<E> entities,
762
                        final InsertsCondition<? super E> condition,
763
                        final InsertsType insertsType) {
764
                var insertedEntities = new ArrayList<E>();
1✔
765
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
766
                        bulkInsert(entityType, entities, condition, insertedEntities);
1✔
767
                } else {
768
                        batchInsert(entityType, entities, condition, insertedEntities);
1✔
769
                }
770
                return insertedEntities.stream();
1✔
771
        }
772

773
        /**
774
         * {@inheritDoc}
775
         *
776
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
777
         */
778
        @Override
779
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
780
                        final Stream<E> entities,
781
                        final InsertsCondition<? super E> condition) {
782
                return insertsAndReturn(entityType, entities, condition, getInsertsType());
1✔
783
        }
784

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

796
        /**
797
         * {@inheritDoc}
798
         *
799
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
800
         */
801
        @Override
802
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
803
                        final Stream<E> entities,
804
                        final InsertsType insertsType) {
805
                return insertsAndReturn(entityType, entities, getInsertsCondition(insertsType), insertsType);
1✔
806
        }
807

808
        /**
809
         * {@inheritDoc}
810
         *
811
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
812
         */
813
        @Override
814
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
815
                        final InsertsCondition<? super E> condition,
816
                        final InsertsType insertsType) {
817
                var iterator = entities.iterator();
1✔
818
                if (!iterator.hasNext()) {
1✔
819
                        return Stream.empty();
1✔
820
                }
821
                var firstEntity = iterator.next();
1✔
822
                @SuppressWarnings("unchecked")
823
                var type = (Class<E>) firstEntity.getClass();
1✔
824
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
825
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
826
                return insertsAndReturn(type, stream, condition, insertsType);
1✔
827
        }
828

829
        /**
830
         * {@inheritDoc}
831
         *
832
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
833
         */
834
        @Override
835
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
836
                        final InsertsCondition<? super E> condition) {
837
                return insertsAndReturn(entities, condition, getInsertsType());
1✔
838
        }
839

840
        /**
841
         * {@inheritDoc}
842
         *
843
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream)
844
         */
845
        @Override
846
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities) {
847
                return insertsAndReturn(entities, getInsertsCondition(getInsertsType()));
1✔
848
        }
849

850
        /**
851
         * {@inheritDoc}
852
         *
853
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
854
         */
855
        @Override
856
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
857
                        final InsertsType insertsType) {
858
                return insertsAndReturn(entities, getInsertsCondition(insertsType), insertsType);
1✔
859
        }
860

861
        /**
862
         * {@inheritDoc}
863
         *
864
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
865
         */
866
        @Override
867
        public <E> int updates(final Class<E> entityType,
868
                        final Stream<E> entities,
869
                        final UpdatesCondition<? super E> condition) {
870
                return batchUpdate(entityType, entities, condition, null);
1✔
871
        }
872

873
        /**
874
         * {@inheritDoc}
875
         *
876
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
877
         */
878
        @Override
879
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
880
                        final Stream<E> entities,
881
                        final UpdatesCondition<? super E> condition) {
882
                var updatedEntities = new ArrayList<E>();
1✔
883
                batchUpdate(entityType, entities, condition, updatedEntities);
1✔
884
                return updatedEntities.stream();
1✔
885
        }
886

887
        /**
888
         * {@inheritDoc}
889
         *
890
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.lang.Class, java.util.stream.Stream)
891
         */
892
        @Override
893
        public <E> int updates(final Class<E> entityType,
894
                        final Stream<E> entities) {
895
                return updates(entityType, entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
896
        }
897

898
        /**
899
         * {@inheritDoc}
900
         *
901
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream)
902
         */
903
        @Override
904
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
905
                        final Stream<E> entities) {
906
                return updatesAndReturn(entityType, entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
907
        }
908

909
        /**
910
         * {@inheritDoc}
911
         *
912
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
913
         */
914
        @Override
915
        public <E> int updates(final Stream<E> entities,
916
                        final UpdatesCondition<? super E> condition) {
917
                var iterator = entities.iterator();
1✔
918
                if (!iterator.hasNext()) {
1✔
919
                        return 0;
×
920
                }
921
                var firstEntity = iterator.next();
1✔
922
                @SuppressWarnings("unchecked")
923
                var type = (Class<E>) firstEntity.getClass();
1✔
924
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
925
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
926
                return updates(type, stream, condition);
1✔
927
        }
928

929
        /**
930
         * {@inheritDoc}
931
         *
932
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
933
         */
934
        @Override
935
        public <E> Stream<E> updatesAndReturn(final Stream<E> entities,
936
                        final UpdatesCondition<? super E> condition) {
937
                var iterator = entities.iterator();
1✔
938
                if (!iterator.hasNext()) {
1✔
939
                        return Stream.empty();
×
940
                }
941
                var firstEntity = iterator.next();
1✔
942
                @SuppressWarnings("unchecked")
943
                var type = (Class<E>) firstEntity.getClass();
1✔
944
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
945
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
946
                return updatesAndReturn(type, stream, condition);
1✔
947
        }
948

949
        /**
950
         * {@inheritDoc}
951
         *
952
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.util.stream.Stream)
953
         */
954
        @Override
955
        public <E> int updates(final Stream<E> entities) {
956
                return updates(entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
957
        }
958

959
        /**
960
         * {@inheritDoc}
961
         *
962
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.util.stream.Stream)
963
         */
964
        @Override
965
        public <E> Stream<E> updatesAndReturn(final Stream<E> entities) {
966
                return updatesAndReturn(entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
967
        }
968

969
        private <E> InsertsCondition<? super E> getInsertsCondition(final InsertsType insertsType) {
970
                return InsertsType.BATCH.equals(insertsType)
1✔
971
                                ? DEFAULT_BATCH_INSERTS_WHEN_CONDITION
1✔
972
                                : DEFAULT_BULK_INSERTS_WHEN_CONDITION;
1✔
973
        }
974

975
        /**
976
         * 複数エンティティのBULK INSERTを実行
977
         *
978
         * @param <E> エンティティの型
979
         * @param entityType エンティティの型
980
         * @param entities エンティティ
981
         * @param condition 一括INSERT用のフレームの判定条件
982
         * @param insertedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
983
         * @return SQL実行結果
984
         */
985
        @SuppressWarnings("deprecation")
986
        private <E> int batchInsert(final Class<E> entityType, final Stream<E> entities,
987
                        final InsertsCondition<? super E> condition, final List<E> insertedEntities) {
988
                EntityHandler<E> handler = this.getEntityHandler();
1✔
989
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
990
                        throw new IllegalArgumentException("Entity type not supported");
1✔
991
                }
992

993
                try {
994
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
995
                        var context = handler.createBatchInsertContext(this, metadata, entityType);
1✔
996
                        context.setSqlKind(SqlKind.ENTITY_BATCH_INSERT);
1✔
997

998
                        var count = 0;
1✔
999
                        var entityList = new ArrayList<E>();
1✔
1000
                        var isFirst = true;
1✔
1001
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
1002
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
1003
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1004
                        var hasBeforeEntityBatchInsertListener = eventListenerHolder.hasBeforeEntityBatchInsertListener();
1✔
1005
                        var eventObj = hasBeforeEntityBatchInsertListener
1✔
1006
                                        ? new BeforeEntityBatchInsertEvent(context, null, entityType)
1✔
1007
                                        : null;
1✔
1008
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1009
                                var entity = iterator.next();
1✔
1010

1011
                                if (!entityType.isInstance(entity)) {
1✔
1012
                                        throw new IllegalArgumentException("Entity types do not match");
1✔
1013
                                }
1014

1015
                                if (isFirst) {
1✔
1016
                                        isFirst = false;
1✔
1017
                                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
1018
                                        autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
1019

1020
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
1021
                                        var excludeColumns = autoGeneratedColumns;
1✔
1022
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
1023
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
1024
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
1025
                                                                        && col.getValue(entity) != null)
1✔
1026
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
1027
                                }
1028

1029
                                entityList.add(entity);
1✔
1030
                                if (insertedEntities != null) {
1✔
1031
                                        insertedEntities.add(entity);
1✔
1032
                                }
1033

1034
                                // EntityBatchInsert実行前イベント発行
1035
                                if (hasBeforeEntityBatchInsertListener) {
1✔
1036
                                        // BatchInsertでは大量のEntityが処理されるため、実行前イベントでは都度Eventインスタンスを生成せず、1つのイベントインスタンスを使いまわす実装とする(性能対応)
1037
                                        eventObj.setEntity(entity);
1✔
1038
                                        for (var listener : eventListenerHolder.getBeforeEntityBatchInsertListeners()) {
1✔
1039
                                                listener.accept(eventObj);
1✔
1040
                                        }
1✔
1041
                                }
1042
                                handler.setInsertParams(context, entity);
1✔
1043

1044
                                context.addBatch();
1✔
1045
                                // SQLのID項目IF分岐判定をtrueにするためにaddBatch()の後に保持しておいたID項目をcontextにバインドする
1046
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
1047
                                        context.paramMap(nonNullObjectIdFlags);
1✔
1048
                                }
1049
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
1050
                                        count += Arrays
1✔
1051
                                                        .stream(doBatchInsert(context, handler, entityList, entityType, autoGeneratedColumns))
1✔
1052
                                                        .sum();
1✔
1053
                                        entityList.clear();
1✔
1054
                                }
1055
                        }
1✔
1056
                        return count + (context.batchCount() != 0
1✔
1057
                                        ? Arrays.stream(doBatchInsert(context, handler, entityList, entityType, autoGeneratedColumns)).sum()
1✔
1058
                                        : 0);
1✔
1059
                } catch (SQLException ex) {
×
1060
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_BATCH_INSERT, ex);
×
1061
                }
1062
        }
1063

1064
        private <E> int[] doBatchInsert(final ExecutionContext context,
1065
                        final EntityHandler<E> handler,
1066
                        final List<E> entityList,
1067
                        final Class<E> entityType,
1068
                        final List<MappingColumn> autoGeneratedColumns)
1069
                        throws SQLException {
1070
                var counts = handler.doBatchInsert(this, context);
1✔
1071

1072
                // EntityBatchInsert実行後イベント発行
1073
                var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1074
                if (eventListenerHolder.hasAfterEntityBatchInsertListener()) {
1✔
1075
                        var eventObj = new AfterEntityBatchInsertEvent(context, entityList, entityType, counts);
1✔
1076
                        for (var listener : eventListenerHolder.getAfterEntityBatchInsertListeners()) {
1✔
1077
                                listener.accept(eventObj);
1✔
1078
                        }
1✔
1079
                        counts = eventObj.getCounts();
1✔
1080
                }
1081

1082
                if (!autoGeneratedColumns.isEmpty()) {
1✔
1083
                        var ids = context.getGeneratedKeyValues();
1✔
1084
                        var idx = 0;
1✔
1085
                        for (E ent : entityList) {
1✔
1086
                                for (var col : autoGeneratedColumns) {
1✔
1087
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
1088
                                }
1✔
1089
                        }
1✔
1090
                }
1091

1092
                return counts;
1✔
1093
        }
1094

1095
        /**
1096
         * 複数エンティティのINSERTをバッチ実行
1097
         *
1098
         * @param <E> エンティティの型
1099
         * @param entityType エンティティの型
1100
         * @param entities エンティティ
1101
         * @param condition 一括INSERT用のフレームの判定条件
1102
         * @param insertedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
1103
         * @return SQL実行結果
1104
         */
1105
        @SuppressWarnings("deprecation")
1106
        private <E> int bulkInsert(final Class<E> entityType, final Stream<E> entities,
1107
                        final InsertsCondition<? super E> condition, final List<E> insertedEntities) {
1108
                EntityHandler<E> handler = this.getEntityHandler();
1✔
1109
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1110
                        throw new IllegalArgumentException("Entity type not supported");
×
1111
                }
1112

1113
                try {
1114
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1115
                        var context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
1116
                        context.setSqlKind(SqlKind.ENTITY_BULK_INSERT);
1✔
1117

1118
                        var frameCount = 0;
1✔
1119
                        var count = 0;
1✔
1120
                        var isFirst = true;
1✔
1121
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
1122
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
1123
                        var entityList = new ArrayList<E>();
1✔
1124
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1125
                        var hasBeforeEntityBulkInsertListener = eventListenerHolder.hasBeforeEntityBulkInsertListener();
1✔
1126
                        var eventObj = hasBeforeEntityBulkInsertListener
1✔
1127
                                        ? new BeforeEntityBulkInsertEvent(context, null, entityType, frameCount)
1✔
1128
                                        : null;
1✔
1129
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1130
                                var entity = iterator.next();
1✔
1131

1132
                                if (!entityType.isInstance(entity)) {
1✔
1133
                                        throw new IllegalArgumentException("Entity types do not match");
×
1134
                                }
1135

1136
                                if (isFirst) {
1✔
1137
                                        isFirst = false;
1✔
1138
                                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
1139
                                        autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
1140

1141
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
1142
                                        var excludeColumns = autoGeneratedColumns;
1✔
1143
                                        // indexなしのid値がcontextにバインドされないため、値としてkey=trueを退避しておく
1144
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
1145
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
1146
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
1147
                                                                        && col.getValue(entity) != null)
1✔
1148
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
1149
                                }
1150
                                // 退避しておいたid値をこのタイミングで設定する
1151
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
1152
                                        context.paramMap(nonNullObjectIdFlags);
1✔
1153
                                }
1154

1155
                                entityList.add(entity);
1✔
1156
                                if (insertedEntities != null) {
1✔
1157
                                        insertedEntities.add(entity);
1✔
1158
                                }
1159

1160
                                // EntityBulkInsert実行前イベント発行
1161
                                if (hasBeforeEntityBulkInsertListener) {
1✔
1162
                                        // BulkInsertでは大量のEntityが処理されるため、実行前イベントでは都度Eventインスタンスを生成せず、1つのイベントインスタンスを使いまわす実装とする(性能対応)
1163
                                        eventObj.setEntity(entity);
1✔
1164
                                        eventObj.setFrameCount(frameCount);
1✔
1165
                                        for (var listener : eventListenerHolder.getBeforeEntityBulkInsertListeners()) {
1✔
1166
                                                listener.accept(eventObj);
1✔
1167
                                        }
1✔
1168
                                }
1169
                                handler.setBulkInsertParams(context, entity, frameCount);
1✔
1170

1171
                                frameCount++;
1✔
1172

1173
                                if (condition.test(context, frameCount, entity)) {
1✔
1174
                                        count += doBulkInsert(context, handler, entityList, entityType, metadata, autoGeneratedColumns,
1✔
1175
                                                        frameCount);
1176
                                        frameCount = 0;
1✔
1177
                                        entityList.clear();
1✔
1178

1179
                                        // 新しいExecutionContextを作成する前にgeneratedKeyColumnsを退避しておく
1180
                                        var generatedKeyColumns = context.getGeneratedKeyColumns();
1✔
1181
                                        context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
1182
                                        context.setSqlKind(SqlKind.ENTITY_BULK_INSERT);
1✔
1183
                                        // 実行結果から生成されたIDを取得できるようにPreparedStatementにIDカラムを渡す
1184
                                        context.setGeneratedKeyColumns(generatedKeyColumns);
1✔
1185
                                }
1186
                        }
1✔
1187
                        return count + (frameCount > 0
1✔
1188
                                        ? doBulkInsert(context, handler, entityList, entityType, metadata, autoGeneratedColumns, frameCount)
1✔
1189
                                        : 0);
1✔
1190

1191
                } catch (SQLException ex) {
×
1192
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_BULK_INSERT, ex);
×
1193
                }
1194
        }
1195

1196
        private <E> int doBulkInsert(final ExecutionContext context,
1197
                        final EntityHandler<E> handler,
1198
                        final List<E> entityList,
1199
                        final Class<E> entityType,
1200
                        final TableMetadata metadata,
1201
                        final List<MappingColumn> autoGeneratedColumns,
1202
                        final int frameCount)
1203
                        throws SQLException {
1204
                var count = handler.doBulkInsert(this,
1✔
1205
                                handler.setupSqlBulkInsertContext(this, context, metadata, entityType, entityList.size()));
1✔
1206

1207
                // EntityBulkInsert実行前イベント発行
1208
                var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1209
                if (eventListenerHolder.hasAfterEntityBulkInsertListener()) {
1✔
1210
                        var eventObj = new AfterEntityBulkInsertEvent(context, entityList, entityType, frameCount, count);
1✔
1211
                        for (var listener : eventListenerHolder.getAfterEntityBulkInsertListeners()) {
1✔
1212
                                listener.accept(eventObj);
1✔
1213
                        }
1✔
1214
                        count = eventObj.getCount();
1✔
1215
                }
1216

1217
                if (!autoGeneratedColumns.isEmpty()) {
1✔
1218
                        var ids = context.getGeneratedKeyValues();
1✔
1219
                        var idx = 0;
1✔
1220
                        for (E ent : entityList) {
1✔
1221
                                for (var col : autoGeneratedColumns) {
1✔
1222
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
1223
                                }
1✔
1224
                        }
1✔
1225
                }
1226

1227
                return count;
1✔
1228
        }
1229

1230
        /**
1231
         * 複数エンティティのBATCH UPDATEを実行
1232
         *
1233
         * @param <E> エンティティの型
1234
         * @param entityType エンティティの型
1235
         * @param entities エンティティ
1236
         * @param condition 一括更新用のフレームの判定条件
1237
         * @param updatedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
1238
         * @return SQL実行結果
1239
         */
1240
        @SuppressWarnings("deprecation")
1241
        private <E> int batchUpdate(final Class<E> entityType, final Stream<E> entities,
1242
                        final UpdatesCondition<? super E> condition, final List<E> updatedEntities) {
1243
                var handler = this.getEntityHandler();
1✔
1244
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1245
                        throw new IllegalArgumentException("Entity type not supported");
×
1246
                }
1247

1248
                try {
1249
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1250
                        var context = handler.createBatchUpdateContext(this, metadata, entityType);
1✔
1251
                        context.setSqlKind(SqlKind.BATCH_UPDATE);
1✔
1252

1253
                        var versionColumn = MappingUtils.getVersionMappingColumn(metadata.getSchema(),
1✔
1254
                                        entityType);
1255
                        var entityCount = 0;
1✔
1256
                        var updateCount = 0;
1✔
1257
                        var entityList = new ArrayList<E>();
1✔
1258
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1259
                        var hasBeforeEntityBatchUpdateListener = eventListenerHolder.hasBeforeEntityBatchUpdateListener();
1✔
1260
                        var eventObj = hasBeforeEntityBatchUpdateListener
1✔
1261
                                        ? new BeforeEntityBatchUpdateEvent(context, null, entityType)
1✔
1262
                                        : null;
1✔
1263
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1264
                                var entity = iterator.next();
1✔
1265

1266
                                if (!entityType.isInstance(entity)) {
1✔
1267
                                        throw new IllegalArgumentException("Entity types do not match");
×
1268
                                }
1269

1270
                                entityList.add(entity);
1✔
1271
                                if (updatedEntities != null) {
1✔
1272
                                        updatedEntities.add(entity);
1✔
1273
                                }
1274
                                entityCount++;
1✔
1275

1276
                                // EntityBatchUpdate実行前イベント発行
1277
                                if (hasBeforeEntityBatchUpdateListener) {
1✔
1278
                                        // BatchUpdateでは大量のEntityが処理されるため、実行前イベントでは都度Eventインスタンスを生成せず、1つのイベントインスタンスを使いまわす実装とする(性能対応)
1279
                                        eventObj.setEntity(entity);
1✔
1280
                                        for (var listener : eventListenerHolder.getBeforeEntityBatchUpdateListeners()) {
1✔
1281
                                                listener.accept(eventObj);
1✔
1282
                                        }
1✔
1283
                                }
1284

1285
                                handler.setUpdateParams(context, entity);
1✔
1286
                                context.addBatch();
1✔
1287

1288
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
1289
                                        updateCount += Arrays.stream(doBatchUpdate(handler, context, entityList, entityType)).sum();
1✔
1290
                                        entityList.clear();
1✔
1291
                                }
1292
                        }
1✔
1293
                        updateCount = updateCount + (context.batchCount() != 0
1✔
1294
                                        ? Arrays.stream(doBatchUpdate(handler, context, entityList, entityType)).sum()
1✔
1295
                                        : 0);
1✔
1296

1297
                        if (updatedEntities != null && versionColumn.isPresent()) {
1✔
1298
                                var vColumn = versionColumn.orElseThrow();
1✔
1299
                                var keyColumns = metadata.getColumns().stream()
1✔
1300
                                                .filter(TableMetadata.Column::isKey)
1✔
1301
                                                .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
1302
                                                .map(c -> MappingUtils.getMappingColumnMap(metadata.getSchema(), entityType, SqlKind.NONE)
1✔
1303
                                                                .get(c.getCamelColumnName()))
1✔
1304
                                                .collect(Collectors.toList());
1✔
1305

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

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

1316
                                        for (var start = 0; start < entitySize; start = start + IN_CLAUSE_MAX_PARAM_SIZE) {
1✔
1317
                                                var end = Math.min(start + IN_CLAUSE_MAX_PARAM_SIZE, entitySize);
1✔
1318
                                                var subList = keyList.subList(start, end);
1✔
1319

1320
                                                query(entityType).in(keyColumn.getCamelName(), subList).stream()
1✔
1321
                                                                .map(e -> {
1✔
1322
                                                                        var updatedEntity = updatedEntityMap.get(keyColumn.getValue(e)).get(0);
1✔
1323
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
1✔
1324
                                                                        return updatedEntity;
1✔
1325
                                                                }).count();
1✔
1326
                                        }
1327
                                } else if (keyColumns.size() > 1) {
1✔
1328
                                        // 複合キーの場合はIN句で一括取得できないため1件ずつ取得して@Versionのついたフィールドを更新する
1329
                                        updatedEntities.stream()
1✔
1330
                                                        .map(updatedEntity -> {
1✔
1331
                                                                var keyValues = keyColumns.stream().map(k -> k.getValue(updatedEntity)).toArray();
×
1332
                                                                find(entityType, keyValues).ifPresent(e -> {
×
1333
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
×
1334
                                                                });
×
1335
                                                                return updatedEntity;
×
1336
                                                        }).count();
1✔
1337
                                }
1338
                        }
1339
                        if (versionColumn.isPresent() && getDialect().supportsEntityBulkUpdateOptimisticLock()
1✔
1340
                                        && updateCount != entityCount) {
1341
                                // バージョンカラムの指定があり、更新件数と更新対象Entityの件数が不一致の場合は楽観ロックエラーとする
1342
                                throw new OptimisticLockException(String.format(
1✔
1343
                                                "An error occurred due to optimistic locking.%nExecuted SQL [%n%s]%nBatch Entity Count: %d, Update Count: %d.",
1344
                                                context.getExecutableSql(), entityCount, updateCount));
1✔
1345
                        }
1346
                        return updateCount;
1✔
1347
                } catch (SQLException ex) {
×
1348
                        throw new EntitySqlRuntimeException(SqlKind.BATCH_UPDATE, ex);
×
1349
                }
1350
        }
1351

1352
        private <E> int[] doBatchUpdate(final EntityHandler<Object> handler,
1353
                        final ExecutionContext context,
1354
                        final List<E> entityList,
1355
                        final Class<E> entityType) throws SQLException {
1356
                var counts = handler.doBatchUpdate(this, context);
1✔
1357

1358
                // EntityBatchUpdate実行後イベント発行
1359
                var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1360
                if (eventListenerHolder.hasAfterEntityBatchUpdateListener()) {
1✔
1361
                        var eventObj = new AfterEntityBatchUpdateEvent(context, entityList, entityType, counts);
1✔
1362
                        for (var listener : eventListenerHolder.getAfterEntityBatchUpdateListeners()) {
1✔
1363
                                listener.accept(eventObj);
1✔
1364
                        }
1✔
1365
                        counts = eventObj.getCounts();
1✔
1366
                }
1367

1368
                return counts;
1✔
1369
        }
1370

1371
        /**
1372
         * {@inheritDoc}
1373
         *
1374
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext)
1375
         */
1376
        @Override
1377
        public ResultSet query(final ExecutionContext executionContext) throws SQLException {
1378
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1379
                        executionContext.setSqlKind(SqlKind.SELECT);
1✔
1380
                }
1381

1382
                // コンテキスト変換
1383
                transformContext(executionContext);
1✔
1384

1385
                var stmt = getPreparedStatement(executionContext);
1✔
1386

1387
                // INパラメータ設定
1388
                executionContext.bindParams(stmt);
1✔
1389

1390
                debugWith(LOG)
1✔
1391
                                .setMessage("Execute query sql. sqlName: {}")
1✔
1392
                                .addArgument(executionContext.getSqlName())
1✔
1393
                                .log();
1✔
1394
                var startTime = PERFORMANCE_LOG.isDebugEnabled() ? Instant.now(getSqlConfig().getClock()) : null;
1✔
1395

1396
                try {
1397
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1398
                        var maxRetryCount = executionContext.getMaxRetryCount() >= 0
1✔
1399
                                        ? executionContext.getMaxRetryCount()
1✔
1400
                                        : getMaxRetryCount();
1✔
1401

1402
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1403
                        var retryWaitTime = executionContext.getRetryWaitTime() > 0
1✔
1404
                                        ? executionContext.getRetryWaitTime()
1✔
1405
                                        : getRetryWaitTime();
1✔
1406
                        var loopCount = 0;
1✔
1407
                        var dialect = getDialect();
1✔
1408
                        ResultSet rs = null;
1✔
1409
                        try {
1410
                                do {
1411
                                        try {
1412
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1413
                                                        setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1414
                                                }
1415
                                                rs = stmt.executeQuery();
1✔
1416
                                                // Query実行後イベント発行
1417
                                                if (getSqlConfig().getEventListenerHolder().hasAfterSqlQueryListener()) {
1✔
1418
                                                        var eventObj = new AfterSqlQueryEvent(executionContext, rs, stmt.getConnection(), stmt);
1✔
1419
                                                        for (var listener : getSqlConfig().getEventListenerHolder().getAfterSqlQueryListeners()) {
1✔
1420
                                                                listener.accept(eventObj);
1✔
1421
                                                        }
1✔
1422
                                                        rs = eventObj.getResultSet();
1✔
1423
                                                }
1424
                                                stmt.closeOnCompletion();
1✔
1425
                                                return new InnerResultSet(rs, stmt);
1✔
1426
                                        } catch (SQLException ex) {
1✔
1427
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1428
                                                        rollback(RETRY_SAVEPOINT_NAME);
1✔
1429
                                                }
1430
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1431
                                                var sqlState = ex.getSQLState();
1✔
1432
                                                var pessimisticLockingErrorCodes = dialect.getPessimisticLockingErrorCodes();
1✔
1433
                                                if (maxRetryCount > loopCount
1✔
1434
                                                                && (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState))) {
1✔
1435
                                                        debugWith(LOG)
1✔
1436
                                                                        .setMessage("Caught the error code to be retried.({} times). Retry after {} ms.")
1✔
1437
                                                                        .addArgument(loopCount + 1)
1✔
1438
                                                                        .addArgument(() -> String.format("%,3d", retryWaitTime))
1✔
1439
                                                                        .log();
1✔
1440
                                                        if (retryWaitTime > 0) {
1✔
1441
                                                                try {
1442
                                                                        Thread.sleep(retryWaitTime);
1✔
1443
                                                                } catch (InterruptedException ie) {
×
1444
                                                                        // do nothing
1445
                                                                }
1✔
1446
                                                        }
1447
                                                } else {
1448
                                                        if (pessimisticLockingErrorCodes.contains(errorCode)
1✔
1449
                                                                        || pessimisticLockingErrorCodes.contains(sqlState)) {
1✔
1450
                                                                throw new PessimisticLockException(executionContext, ex);
1✔
1451
                                                        } else {
1452
                                                                throw ex;
1✔
1453
                                                        }
1454
                                                }
1455
                                        } finally {
1456
                                                if (maxRetryCount > 0 && dialect.isRollbackToSavepointBeforeRetry()) {
1✔
1457
                                                        releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1458
                                                }
1459
                                                executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1460
                                        }
1461
                                } while (maxRetryCount > loopCount++);
1✔
1462
                        } catch (SQLException | RuntimeException ex) {
1✔
1463
                                if (rs != null && !rs.isClosed()) {
1✔
1464
                                        rs.close();
1✔
1465
                                }
1466
                                throw ex;
1✔
1467
                        }
×
1468
                        return null;
×
1469
                } catch (SQLException ex) {
1✔
1470
                        handleException(executionContext, ex);
×
1471
                        return null;
×
1472
                } finally {
1473
                        // 後処理
1474
                        debugWith(PERFORMANCE_LOG)
1✔
1475
                                        .setMessage("SQL execution time [{}({})] : [{}]")
1✔
1476
                                        .addArgument(() -> generateSqlName(executionContext))
1✔
1477
                                        .addArgument(executionContext.getSqlKind())
1✔
1478
                                        .addArgument(() -> formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())))
1✔
1479
                                        .log();
1✔
1480
                }
1481
        }
1482

1483
        /**
1484
         * {@inheritDoc}
1485
         *
1486
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1487
         *      jp.co.future.uroborosql.converter.ResultSetConverter)
1488
         */
1489
        @Override
1490
        public <T> Stream<T> query(final ExecutionContext executionContext, final ResultSetConverter<T> converter)
1491
                        throws SQLException {
1492
                var rs = query(executionContext);
1✔
1493
                return StreamSupport.stream(new ResultSetSpliterator<>(rs, converter), false).onClose(() -> {
1✔
1494
                        try {
1495
                                if (rs != null && !rs.isClosed()) {
1✔
1496
                                        rs.close();
1✔
1497
                                }
1498
                        } catch (SQLException ex) {
×
1499
                                // do nothing
1500
                        }
1✔
1501
                });
1✔
1502
        }
1503

1504
        /**
1505
         * {@inheritDoc}
1506
         *
1507
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1508
         *      jp.co.future.uroborosql.utils.CaseFormat)
1509
         */
1510
        @Override
1511
        public List<Map<String, Object>> query(final ExecutionContext executionContext, final CaseFormat caseFormat)
1512
                        throws SQLException {
1513
                try (var stream = query(executionContext, new MapResultSetConverter(getSqlConfig(), caseFormat))) {
1✔
1514
                        return stream.collect(Collectors.toList());
1✔
1515
                }
1516
        }
1517

1518
        /**
1519
         * @see jp.co.future.uroborosql.SqlAgent#update(jp.co.future.uroborosql.context.ExecutionContext)
1520
         */
1521
        @Override
1522
        public int update(final ExecutionContext executionContext) throws SQLException {
1523
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1524
                        executionContext.setSqlKind(SqlKind.UPDATE);
1✔
1525
                }
1526

1527
                // コンテキスト変換
1528
                transformContext(executionContext);
1✔
1529

1530
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1531
                if (executionContext.getUpdateDelegate() != null) {
1✔
1532
                        debugWith(LOG)
1✔
1533
                                        .log("Performs update delegate of update process.");
1✔
1534
                        return executionContext.getUpdateDelegate().apply(executionContext);
1✔
1535
                }
1536

1537
                Instant startTime = null;
1✔
1538

1539
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1540

1541
                        // INパラメータ設定
1542
                        executionContext.bindParams(stmt);
1✔
1543

1544
                        debugWith(LOG)
1✔
1545
                                        .setMessage("Execute update sql. sqlName: {}")
1✔
1546
                                        .addArgument(executionContext.getSqlName())
1✔
1547
                                        .log();
1✔
1548

1549
                        if (PERFORMANCE_LOG.isDebugEnabled()) {
1✔
1550
                                startTime = Instant.now(getSqlConfig().getClock());
×
1551
                        }
1552

1553
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1554
                        var maxRetryCount = executionContext.getMaxRetryCount() >= 0
1✔
1555
                                        ? executionContext.getMaxRetryCount()
1✔
1556
                                        : getMaxRetryCount();
1✔
1557

1558
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1559
                        var retryWaitTime = executionContext.getRetryWaitTime() > 0
1✔
1560
                                        ? executionContext.getRetryWaitTime()
1✔
1561
                                        : getRetryWaitTime();
1✔
1562
                        var loopCount = 0;
1✔
1563
                        do {
1564
                                try {
1565
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1566
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1567
                                        }
1568
                                        var count = stmt.executeUpdate();
1✔
1569
                                        // Update実行後イベント発行
1570
                                        if (getSqlConfig().getEventListenerHolder().hasAfterSqlUpdateListener()) {
1✔
1571
                                                var eventObj = new AfterSqlUpdateEvent(executionContext, count, stmt.getConnection(), stmt);
1✔
1572
                                                for (var listener : getSqlConfig().getEventListenerHolder().getAfterSqlUpdateListeners()) {
1✔
1573
                                                        listener.accept(eventObj);
1✔
1574
                                                }
1✔
1575
                                                count = eventObj.getCount();
1✔
1576
                                        }
1577
                                        if ((SqlKind.INSERT.equals(executionContext.getSqlKind()) ||
1✔
1578
                                                        SqlKind.ENTITY_INSERT.equals(executionContext.getSqlKind()) ||
1✔
1579
                                                        SqlKind.BULK_INSERT.equals(executionContext.getSqlKind()) ||
1✔
1580
                                                        SqlKind.ENTITY_BULK_INSERT.equals(executionContext.getSqlKind()))
1✔
1581
                                                        && executionContext.hasGeneratedKeyColumns()) {
1✔
1582
                                                try (var rs = stmt.getGeneratedKeys()) {
1✔
1583
                                                        var generatedKeyValues = new ArrayList<>();
1✔
1584
                                                        while (rs.next()) {
1✔
1585
                                                                for (var i = 1; i <= executionContext.getGeneratedKeyColumns().length; i++) {
1✔
1586
                                                                        generatedKeyValues.add(rs.getObject(i));
1✔
1587
                                                                }
1588
                                                        }
1589
                                                        executionContext.setGeneratedKeyValues(
1✔
1590
                                                                        generatedKeyValues.toArray(new Object[generatedKeyValues.size()]));
1✔
1591
                                                }
1592
                                        }
1593
                                        return count;
1✔
1594
                                } catch (SQLException ex) {
1✔
1595
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1596
                                                rollback(RETRY_SAVEPOINT_NAME);
1✔
1597
                                        }
1598
                                        if (maxRetryCount > loopCount) {
1✔
1599
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1600
                                                var sqlState = ex.getSQLState();
1✔
1601
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1602
                                                        debugWith(LOG)
1✔
1603
                                                                        .setMessage("Caught the error code to be retried.({} times). Retry after {} ms.")
1✔
1604
                                                                        .addArgument(loopCount + 1)
1✔
1605
                                                                        .addArgument(() -> String.format("%,3d", retryWaitTime))
1✔
1606
                                                                        .log();
1✔
1607
                                                        if (retryWaitTime > 0) {
1✔
1608
                                                                try {
1609
                                                                        Thread.sleep(retryWaitTime);
1✔
1610
                                                                } catch (InterruptedException ie) {
×
1611
                                                                        // do nothing
1612
                                                                }
1✔
1613
                                                        }
1614
                                                } else {
1615
                                                        throw ex;
1✔
1616
                                                }
1617
                                        } else {
1✔
1618
                                                throw ex;
1✔
1619
                                        }
1620
                                } finally {
1621
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1622
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1623
                                        }
1624
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1625
                                }
1626
                        } while (maxRetryCount > loopCount++);
1✔
1627
                        return 0;
×
1628
                } catch (SQLException ex) {
1✔
1629
                        handleException(executionContext, ex);
×
1630
                        return 0;
×
1631
                } finally {
1632
                        // 後処理
1633
                        var curStartTime = startTime;
1✔
1634
                        debugWith(PERFORMANCE_LOG)
1✔
1635
                                        .setMessage("SQL execution time [{}({})] : [{}]")
1✔
1636
                                        .addArgument(() -> generateSqlName(executionContext))
1✔
1637
                                        .addArgument(executionContext.getSqlKind())
1✔
1638
                                        .addArgument(() -> formatElapsedTime(curStartTime, Instant.now(getSqlConfig().getClock())))
1✔
1639
                                        .log();
1✔
1640
                }
1641
        }
1642

1643
        /**
1644
         * @see jp.co.future.uroborosql.SqlAgent#batch(jp.co.future.uroborosql.context.ExecutionContext)
1645
         */
1646
        @Override
1647
        public int[] batch(final ExecutionContext executionContext) throws SQLException {
1648
                // バッチ処理の場合大量のログが出力されるため、パラメータログの出力を抑止する
1649
                suppressParameterLogging();
1✔
1650

1651
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1652
                        executionContext.setSqlKind(SqlKind.BATCH_INSERT);
1✔
1653
                }
1654

1655
                // コンテキスト変換
1656
                transformContext(executionContext);
1✔
1657

1658
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1659
                if (executionContext.getUpdateDelegate() != null) {
1✔
1660
                        releaseParameterLogging();
1✔
1661
                        debugWith(LOG)
1✔
1662
                                        .log("Performs update delegate of batch process.");
1✔
1663
                        return new int[] { executionContext.getUpdateDelegate().apply(executionContext) };
1✔
1664
                }
1665

1666
                Instant startTime = null;
1✔
1667

1668
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1669

1670
                        // INパラメータ設定
1671
                        executionContext.bindBatchParams(stmt);
1✔
1672

1673
                        debugWith(LOG)
1✔
1674
                                        .setMessage("Execute batch sql. sqlName: {}")
1✔
1675
                                        .addArgument(executionContext.getSqlName())
1✔
1676
                                        .log();
1✔
1677
                        if (PERFORMANCE_LOG.isDebugEnabled()) {
1✔
1678
                                startTime = Instant.now(getSqlConfig().getClock());
×
1679
                        }
1680

1681
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1682
                        var maxRetryCount = executionContext.getMaxRetryCount() >= 0
1✔
1683
                                        ? executionContext.getMaxRetryCount()
1✔
1684
                                        : getMaxRetryCount();
1✔
1685

1686
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1687
                        var retryWaitTime = executionContext.getRetryWaitTime() > 0
1✔
1688
                                        ? executionContext.getRetryWaitTime()
×
1689
                                        : getRetryWaitTime();
1✔
1690
                        var loopCount = 0;
1✔
1691
                        do {
1692
                                try {
1693
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1694
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
×
1695
                                        }
1696
                                        var counts = stmt.executeBatch();
1✔
1697
                                        // Batch実行後イベント発行
1698
                                        if (getSqlConfig().getEventListenerHolder().hasAfterSqlBatchListener()) {
1✔
1699
                                                var eventObj = new AfterSqlBatchEvent(executionContext, counts, stmt.getConnection(), stmt);
1✔
1700
                                                for (var listener : getSqlConfig().getEventListenerHolder().getAfterSqlBatchListeners()) {
1✔
1701
                                                        listener.accept(eventObj);
1✔
1702
                                                }
1✔
1703
                                                counts = eventObj.getCounts();
1✔
1704
                                        }
1705
                                        if ((SqlKind.BATCH_INSERT.equals(executionContext.getSqlKind()) ||
1✔
1706
                                                        SqlKind.ENTITY_BATCH_INSERT.equals(executionContext.getSqlKind()))
1✔
1707
                                                        && executionContext.hasGeneratedKeyColumns()) {
1✔
1708
                                                try (var rs = stmt.getGeneratedKeys()) {
1✔
1709
                                                        var generatedKeyValues = new ArrayList<>();
1✔
1710
                                                        while (rs.next()) {
1✔
1711
                                                                for (var i = 1; i <= executionContext.getGeneratedKeyColumns().length; i++) {
1✔
1712
                                                                        generatedKeyValues.add(rs.getObject(i));
1✔
1713
                                                                }
1714
                                                        }
1715
                                                        executionContext.setGeneratedKeyValues(
1✔
1716
                                                                        generatedKeyValues.toArray(new Object[generatedKeyValues.size()]));
1✔
1717
                                                }
1718
                                        }
1719
                                        return counts;
1✔
1720
                                } catch (SQLException ex) {
1✔
1721
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1722
                                                rollback(RETRY_SAVEPOINT_NAME);
×
1723
                                        }
1724
                                        if (maxRetryCount > loopCount) {
1✔
1725
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1726
                                                var sqlState = ex.getSQLState();
1✔
1727
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1728
                                                        debugWith(LOG)
1✔
1729
                                                                        .setMessage("Caught the error code to be retried.({} times). Retry after {} ms.")
1✔
1730
                                                                        .addArgument(loopCount + 1)
1✔
1731
                                                                        .addArgument(() -> String.format("%,3d", retryWaitTime))
1✔
1732
                                                                        .log();
1✔
1733
                                                        if (retryWaitTime > 0) {
1✔
1734
                                                                try {
1735
                                                                        Thread.sleep(retryWaitTime);
1✔
1736
                                                                } catch (InterruptedException ie) {
×
1737
                                                                        // do nothing
1738
                                                                }
1✔
1739
                                                        }
1740
                                                } else {
1741
                                                        throw ex;
×
1742
                                                }
1743
                                        } else {
1✔
1744
                                                throw ex;
1✔
1745
                                        }
1746
                                } finally {
1747
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1748
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
×
1749
                                        }
1750
                                        executionContext.clearBatch();
1✔
1751
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1752
                                }
1753
                        } while (maxRetryCount > loopCount++);
1✔
1754
                        return null;
×
1755
                } catch (SQLException ex) {
1✔
1756
                        handleException(executionContext, ex);
×
1757
                        return null;
×
1758
                } finally {
1759
                        // 後処理
1760
                        var curStartTime = startTime;
1✔
1761
                        debugWith(PERFORMANCE_LOG)
1✔
1762
                                        .setMessage("SQL execution time [{}({})] : [{}]")
1✔
1763
                                        .addArgument(() -> generateSqlName(executionContext))
1✔
1764
                                        .addArgument(executionContext.getSqlKind())
1✔
1765
                                        .addArgument(() -> formatElapsedTime(curStartTime, Instant.now(getSqlConfig().getClock())))
1✔
1766
                                        .log();
1✔
1767
                        releaseParameterLogging();
1✔
1768
                }
1769
        }
1770

1771
        /**
1772
         * {@inheritDoc}
1773
         *
1774
         * @see jp.co.future.uroborosql.SqlAgent#procedure(jp.co.future.uroborosql.context.ExecutionContext)
1775
         */
1776
        @Override
1777
        public Map<String, Object> procedure(final ExecutionContext executionContext) throws SQLException {
1778
                // procedureやfunctionの場合、SQL文法エラーになるためバインドパラメータコメントを出力しない
1779
                executionContext.contextAttrs().put(CTX_ATTR_KEY_OUTPUT_BIND_COMMENT, false);
1✔
1780

1781
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1782
                        executionContext.setSqlKind(SqlKind.PROCEDURE);
1✔
1783
                }
1784

1785
                // コンテキスト変換
1786
                transformContext(executionContext);
1✔
1787

1788
                Instant startTime = null;
1✔
1789

1790
                try (var callableStatement = getCallableStatement(executionContext)) {
1✔
1791

1792
                        // パラメータ設定
1793
                        executionContext.bindParams(callableStatement);
1✔
1794

1795
                        debugWith(LOG)
1✔
1796
                                        .setMessage("Execute stored procedure. sqlName: {}")
1✔
1797
                                        .addArgument(executionContext.getSqlName())
1✔
1798
                                        .log();
1✔
1799
                        if (PERFORMANCE_LOG.isDebugEnabled()) {
1✔
1800
                                startTime = Instant.now(getSqlConfig().getClock());
×
1801
                        }
1802

1803
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1804
                        var maxRetryCount = executionContext.getMaxRetryCount() >= 0
1✔
1805
                                        ? executionContext.getMaxRetryCount()
1✔
1806
                                        : getMaxRetryCount();
1✔
1807

1808
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1809
                        var retryWaitTime = executionContext.getRetryWaitTime() > 0
1✔
1810
                                        ? executionContext.getRetryWaitTime()
1✔
1811
                                        : getRetryWaitTime();
1✔
1812

1813
                        var loopCount = 0;
1✔
1814
                        do {
1815
                                try {
1816
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1817
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1818
                                        }
1819
                                        var result = callableStatement.execute();
1✔
1820
                                        // Procedure実行後イベント発行
1821
                                        if (getSqlConfig().getEventListenerHolder().hasAfterProcedureListener()) {
1✔
1822
                                                var eventObj = new AfterProcedureEvent(executionContext, result,
1✔
1823
                                                                callableStatement.getConnection(),
1✔
1824
                                                                callableStatement);
1825
                                                for (var listener : getSqlConfig().getEventListenerHolder().getAfterProcedureListeners()) {
1✔
1826
                                                        listener.accept(eventObj);
1✔
1827
                                                }
1✔
1828
                                                result = eventObj.isResult();
1✔
1829
                                        }
1830
                                        break;
1831
                                } catch (SQLException ex) {
1✔
1832
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1833
                                                rollback(RETRY_SAVEPOINT_NAME);
1✔
1834
                                        }
1835
                                        if (maxRetryCount > loopCount) {
1✔
1836
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1837
                                                var sqlState = ex.getSQLState();
1✔
1838
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1839
                                                        debugWith(LOG)
1✔
1840
                                                                        .setMessage("Caught the error code to be retried.({} times). Retry after {} ms.")
1✔
1841
                                                                        .addArgument(loopCount + 1)
1✔
1842
                                                                        .addArgument(() -> String.format("%,3d", retryWaitTime))
1✔
1843
                                                                        .log();
1✔
1844
                                                        if (retryWaitTime > 0) {
1✔
1845
                                                                try {
1846
                                                                        Thread.sleep(retryWaitTime);
1✔
1847
                                                                } catch (InterruptedException ie) {
×
1848
                                                                        // do nothing
1849
                                                                }
1✔
1850
                                                        }
1851
                                                } else {
1852
                                                        throw ex;
1✔
1853
                                                }
1854
                                        } else {
1✔
1855
                                                throw ex;
1✔
1856
                                        }
1857
                                } finally {
1858
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1859
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1860
                                        }
1861
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1862
                                }
1863
                        } while (maxRetryCount > loopCount++);
1✔
1864
                        // 結果取得
1865
                        return executionContext.getOutParams(callableStatement);
1✔
1866
                } catch (SQLException ex) {
1✔
1867
                        handleException(executionContext, ex);
×
1868
                } finally {
1869
                        // 後処理
1870
                        var curStartTime = startTime;
1✔
1871
                        debugWith(PERFORMANCE_LOG)
1✔
1872
                                        .setMessage("Stored procedure execution time [{}({})] : [{}]")
1✔
1873
                                        .addArgument(() -> generateSqlName(executionContext))
1✔
1874
                                        .addArgument(executionContext.getSqlKind())
1✔
1875
                                        .addArgument(() -> formatElapsedTime(curStartTime, Instant.now(getSqlConfig().getClock())))
1✔
1876
                                        .log();
1✔
1877
                }
1878
                return null;
×
1879
        }
1880

1881
        /**
1882
         * ExecutionContextの設定内容を元にSQLを構築する
1883
         *
1884
         * @param executionContext ExecutionContext
1885
         */
1886
        private void transformContext(final ExecutionContext executionContext) {
1887
                var originalSql = executionContext.getSql();
1✔
1888
                var sqlName = executionContext.getSqlName();
1✔
1889
                if (ObjectUtils.isEmpty(originalSql) && getSqlResourceManager() != null) {
1✔
1890
                        originalSql = getSqlResourceManager().getSql(sqlName);
1✔
1891
                        if (ObjectUtils.isEmpty(originalSql)) {
1✔
1892
                                throw new UroborosqlRuntimeException("sql file:[" + sqlName + "] is not found.");
×
1893
                        }
1894
                }
1895

1896
                // SQL-IDの付与
1897
                if (originalSql.contains(keySqlId)) {
1✔
1898
                        var sqlId = executionContext.getSqlId();
1✔
1899
                        if (ObjectUtils.isEmpty(sqlId)) {
1✔
1900
                                sqlId = sqlName;
1✔
1901
                        }
1902
                        if (ObjectUtils.isEmpty(sqlId)) {
1✔
1903
                                sqlId = String.valueOf(originalSql.hashCode());
×
1904
                        }
1905

1906
                        originalSql = originalSql.replace(keySqlId, sqlId);
1✔
1907
                }
1908

1909
                if (SQL_LOG.isDebugEnabled() && sqlName != null) {
1✔
1910
                        if (executionContext.getSqlKind().isEntityType()) {
×
1911
                                debugWith(SQL_LOG)
×
1912
                                                .setMessage("EntityClass : {}")
×
1913
                                                .addArgument(sqlName)
×
1914
                                                .log();
×
1915
                        } else if (getSqlResourceManager().existSql(sqlName)) {
×
1916
                                debugWith(SQL_LOG)
×
1917
                                                .setMessage("SQLUrl : {}")
×
1918
                                                .addArgument(() -> getSqlResourceManager().getSqlUrl(sqlName))
×
1919
                                                .log();
×
1920
                        }
1921
                }
1922

1923
                // SQL変換前イベント発行
1924
                if (getSqlConfig().getEventListenerHolder().hasBeforeTransformSqlListener()) {
1✔
1925
                        var eventObj = new BeforeTransformSqlEvent(executionContext, originalSql);
1✔
1926
                        getSqlConfig().getEventListenerHolder().getBeforeTransformSqlListeners()
1✔
1927
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1928
                        originalSql = eventObj.getSql();
1✔
1929
                }
1930
                executionContext.setSql(originalSql);
1✔
1931

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

1935
                // SQLパース前イベントの呼出
1936
                if (executionContext.batchCount() == 0
1✔
1937
                                && getSqlConfig().getEventListenerHolder().hasBeforeParseSqlListener()) {
1✔
1938
                        var eventObj = new BeforeParseSqlEvent(executionContext);
1✔
1939
                        getSqlConfig().getEventListenerHolder().getBeforeParseSqlListeners()
1✔
1940
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1941
                }
1942

1943
                traceWith(SQL_LOG)
1✔
1944
                                .setMessage("Template SQL[{}{}{}]")
1✔
1945
                                .addArgument(System.lineSeparator())
1✔
1946
                                .addArgument(originalSql)
1✔
1947
                                .addArgument(System.lineSeparator())
1✔
1948
                                .log();
1✔
1949

1950
                if (ObjectUtils.isEmpty(executionContext.getExecutableSql())) {
1✔
1951
                        // SQLパーサーによるパース処理
1952
                        var outputBindComment = (boolean) executionContext.contextAttrs().getOrDefault(
1✔
1953
                                        CTX_ATTR_KEY_OUTPUT_BIND_COMMENT, true);
1✔
1954
                        var sqlParser = new SqlParserImpl(originalSql, sqlConfig.getExpressionParser(),
1✔
1955
                                        getDialect().isRemoveTerminator(), outputBindComment);
1✔
1956
                        var contextTransformer = sqlParser.parse();
1✔
1957
                        contextTransformer.transform(executionContext);
1✔
1958

1959
                        if (COVERAGE_HANDLER_REF.get() != null) {
1✔
1960
                                // SQLカバレッジ用のログを出力する
1961
                                var coverageData = new CoverageData(sqlName, originalSql,
1✔
1962
                                                contextTransformer.getPassedRoute());
1✔
1963
                                traceWith(COVERAGE_LOG)
1✔
1964
                                                .setMessage("coverage data: {}")
1✔
1965
                                                .addArgument(coverageData)
1✔
1966
                                                .log();
1✔
1967

1968
                                COVERAGE_HANDLER_REF.get().accept(coverageData);
1✔
1969
                        }
1970
                }
1971

1972
                debugWith(SQL_LOG)
1✔
1973
                                .setMessage("Executed SQL[{}{}{}]")
1✔
1974
                                .addArgument(System.lineSeparator())
1✔
1975
                                .addArgument(executionContext.getExecutableSql())
1✔
1976
                                .addArgument(System.lineSeparator())
1✔
1977
                                .log();
1✔
1978
        }
1✔
1979

1980
        /** 時間計測用のログに出力するSQL名を生成する.
1981
         *
1982
         * @param executionContext ExecutionContext
1983
         * @return SQL名. SQL名が取得できない場合はSQL_ID、または空文字を返却する
1984
         */
1985
        private String generateSqlName(final ExecutionContext executionContext) {
1986
                if (executionContext.getSqlName() != null) {
×
1987
                        return executionContext.getSqlName();
×
1988
                } else {
1989
                        return Objects.toString(executionContext.getSqlId(), "");
×
1990
                }
1991
        }
1992

1993
        /**
1994
         * 例外発生時ハンドラー
1995
         *
1996
         * @param executionContext ExecutionContext
1997
         * @param ex SQL例外
1998
         * @throws SQLException SQL例外
1999
         */
2000
        private void handleException(final ExecutionContext executionContext, final SQLException ex) throws SQLException {
2001
                var cause = ex;
1✔
2002

2003
                while (cause.getNextException() != null) {
1✔
2004
                        cause = cause.getNextException();
1✔
2005
                }
2006

2007
                if (outputExceptionLog) {
1✔
2008
                        errorWith(LOG)
1✔
2009
                                        .setMessage(() -> {
1✔
2010
                                                var builder = new StringBuilder();
1✔
2011
                                                builder.append(System.lineSeparator()).append("Exception occurred in SQL execution.")
1✔
2012
                                                                .append(System.lineSeparator());
1✔
2013
                                                builder.append("Executed SQL[").append(executionContext.getExecutableSql()).append("]")
1✔
2014
                                                                .append(System.lineSeparator());
1✔
2015
                                                if (executionContext instanceof ExecutionContextImpl) {
1✔
2016
                                                        var bindParameters = ((ExecutionContextImpl) executionContext).getBindParameters();
1✔
2017
                                                        for (var i = 0; i < bindParameters.length; i++) {
1✔
2018
                                                                var parameter = bindParameters[i];
1✔
2019
                                                                builder.append("Bind Parameter.[INDEX[").append(i + 1).append("], ")
1✔
2020
                                                                                .append(parameter.toString())
1✔
2021
                                                                                .append("]").append(System.lineSeparator());
1✔
2022
                                                        }
2023
                                                }
2024
                                                return builder.toString();
1✔
2025
                                        })
2026
                                        .setCause(cause)
1✔
2027
                                        .log();
1✔
2028
                }
2029

2030
                throw cause;
1✔
2031
        }
2032

2033
        /**
2034
         *
2035
         * {@inheritDoc}
2036
         *
2037
         * @see jp.co.future.uroborosql.SqlAgent#find(java.lang.Class, java.lang.Object[])
2038
         */
2039
        @SuppressWarnings("unchecked")
2040
        @Override
2041
        public <E> Optional<E> find(final Class<? extends E> entityType, final Object... keys) {
2042
                @SuppressWarnings("rawtypes")
2043
                EntityHandler handler = this.getEntityHandler();
1✔
2044
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2045
                        throw new IllegalArgumentException("Entity type not supported");
×
2046
                }
2047

2048
                try {
2049
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2050
                        var keyNames = metadata.getColumns().stream()
1✔
2051
                                        .filter(TableMetadata.Column::isKey)
1✔
2052
                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
2053
                                        .map(TableMetadata.Column::getColumnName)
1✔
2054
                                        .map(CaseFormat.CAMEL_CASE::convert)
1✔
2055
                                        .toArray(String[]::new);
1✔
2056

2057
                        if (keyNames.length != keys.length) {
1✔
2058
                                throw new IllegalArgumentException("Number of keys does not match");
×
2059
                        }
2060
                        var params = new HashMap<String, Object>();
1✔
2061
                        for (var i = 0; i < keys.length; i++) {
1✔
2062
                                params.put(keyNames[i], keys[i]);
1✔
2063
                        }
2064

2065
                        var context = handler.createSelectContext(this, metadata, entityType, true);
1✔
2066
                        context.setSqlKind(SqlKind.ENTITY_SELECT).paramMap(params);
1✔
2067

2068
                        // EntityQuery実行前イベント発行
2069
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2070
                        if (eventListenerHolder.hasBeforeEntityQueryListener()) {
1✔
2071
                                var eventObj = new BeforeEntityQueryEvent(context, null, entityType);
×
2072
                                for (var listener : eventListenerHolder.getBeforeEntityQueryListeners()) {
×
2073
                                        listener.accept(eventObj);
×
2074
                                }
×
2075
                        }
2076

2077
                        var results = handler.doSelect(this, context, entityType);
1✔
2078

2079
                        // EntityQuery実行後イベント発行
2080
                        if (eventListenerHolder.hasAfterEntityQueryListener()) {
1✔
2081
                                var eventObj = new AfterEntityQueryEvent(context(), null, entityType, results);
×
2082
                                for (var listener : eventListenerHolder.getAfterEntityQueryListeners()) {
×
2083
                                        listener.accept(eventObj);
×
2084
                                }
×
2085
                                results = eventObj.getResults();
×
2086
                        }
2087

2088
                        try (var stream = results) {
1✔
2089
                                return stream.findFirst();
1✔
2090
                        }
2091
                } catch (SQLException ex) {
×
2092
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_SELECT, ex);
×
2093
                }
2094
        }
2095

2096
        /**
2097
         * {@inheritDoc}
2098
         *
2099
         * @see jp.co.future.uroborosql.SqlAgent#insert(java.lang.Object)
2100
         */
2101
        @SuppressWarnings("unchecked")
2102
        @Override
2103
        public <E> int insert(final E entity) {
2104
                if (entity instanceof Stream) {
1✔
2105
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2106
                }
2107

2108
                @SuppressWarnings("rawtypes")
2109
                EntityHandler handler = this.getEntityHandler();
1✔
2110
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2111
                        throw new IllegalArgumentException("Entity type not supported");
×
2112
                }
2113

2114
                try {
2115
                        var entityType = entity.getClass();
1✔
2116
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2117
                        var context = handler.createInsertContext(this, metadata, entityType);
1✔
2118
                        context.setSqlKind(SqlKind.ENTITY_INSERT);
1✔
2119

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

2124
                        // EntityInsert実行前イベント発行
2125
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2126
                        if (eventListenerHolder.hasBeforeEntityInsertListener()) {
1✔
2127
                                var eventObj = new BeforeEntityInsertEvent(context, entity, entityType);
1✔
2128
                                for (var listener : eventListenerHolder.getBeforeEntityInsertListeners()) {
1✔
2129
                                        listener.accept(eventObj);
1✔
2130
                                }
1✔
2131
                        }
2132
                        handler.setInsertParams(context, entity);
1✔
2133

2134
                        var count = handler.doInsert(this, context, entity);
1✔
2135

2136
                        // EntityInsert実行後イベント発行
2137
                        if (eventListenerHolder.hasAfterEntityInsertListener()) {
1✔
2138
                                var eventObj = new AfterEntityInsertEvent(context, entity, entityType, count);
1✔
2139
                                for (var listener : eventListenerHolder.getAfterEntityInsertListeners()) {
1✔
2140
                                        listener.accept(eventObj);
1✔
2141
                                }
1✔
2142
                                count = eventObj.getCount();
1✔
2143
                        }
2144

2145
                        if (!autoGeneratedColumns.isEmpty()) {
1✔
2146
                                var ids = context.getGeneratedKeyValues();
1✔
2147
                                var idx = 0;
1✔
2148
                                for (var col : autoGeneratedColumns) {
1✔
2149
                                        setEntityIdValue(entity, ids[idx++], col);
1✔
2150
                                }
1✔
2151
                        }
2152
                        return count;
1✔
2153
                } catch (SQLException ex) {
1✔
2154
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_INSERT, ex);
1✔
2155
                }
2156
        }
2157

2158
        /**
2159
         * {@inheritDoc}
2160
         *
2161
         * @see jp.co.future.uroborosql.SqlAgent#insertAndReturn(java.lang.Object)
2162
         */
2163
        @Override
2164
        public <E> E insertAndReturn(final E entity) {
2165
                insert(entity);
1✔
2166
                return entity;
1✔
2167
        }
2168

2169
        /**
2170
         * {@inheritDoc}
2171
         *
2172
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.Object)
2173
         */
2174
        @SuppressWarnings("unchecked")
2175
        @Override
2176
        public <E> int update(final E entity) {
2177
                if (entity instanceof Stream) {
1✔
2178
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2179
                }
2180

2181
                @SuppressWarnings("rawtypes")
2182
                EntityHandler handler = this.getEntityHandler();
1✔
2183
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2184
                        throw new IllegalArgumentException("Entity type not supported");
×
2185
                }
2186

2187
                try {
2188
                        var entityType = entity.getClass();
1✔
2189
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2190
                        var context = handler.createUpdateContext(this, metadata, entityType, true);
1✔
2191
                        context.setSqlKind(SqlKind.ENTITY_UPDATE);
1✔
2192

2193
                        // EntityUpdate実行前イベント発行
2194
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2195
                        if (eventListenerHolder.hasBeforeEntityUpdateListener()) {
1✔
2196
                                var eventObj = new BeforeEntityUpdateEvent(context, entity, entityType);
1✔
2197
                                for (var listener : eventListenerHolder.getBeforeEntityUpdateListeners()) {
1✔
2198
                                        listener.accept(eventObj);
1✔
2199
                                }
1✔
2200
                        }
2201
                        handler.setUpdateParams(context, entity);
1✔
2202

2203
                        var count = handler.doUpdate(this, context, entity);
1✔
2204

2205
                        // EntityUpdate実行後イベント発行
2206
                        if (eventListenerHolder.hasAfterEntityUpdateListener()) {
1✔
2207
                                var eventObj = new AfterEntityUpdateEvent(context, entity, entityType, count);
1✔
2208
                                for (var listener : eventListenerHolder.getAfterEntityUpdateListeners()) {
1✔
2209
                                        listener.accept(eventObj);
1✔
2210
                                }
1✔
2211
                                count = eventObj.getCount();
1✔
2212
                        }
2213

2214
                        var updateCount = count;
1✔
2215
                        MappingUtils.getVersionMappingColumn(metadata.getSchema(), entityType).ifPresent(versionColumn -> {
1✔
2216
                                if (updateCount == 0) {
1✔
2217
                                        throw new OptimisticLockException(context);
1✔
2218
                                } else {
2219
                                        var columnMap = MappingUtils.getMappingColumnMap(metadata.getSchema(), entityType, SqlKind.NONE);
1✔
2220
                                        var keys = metadata.getColumns().stream()
1✔
2221
                                                        .filter(TableMetadata.Column::isKey)
1✔
2222
                                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
2223
                                                        .map(c -> columnMap.get(c.getCamelColumnName()).getValue(entity))
1✔
2224
                                                        .toArray();
1✔
2225

2226
                                        find(entityType, keys).ifPresent(e -> versionColumn.setValue(entity, versionColumn.getValue(e)));
1✔
2227
                                }
2228
                        });
1✔
2229
                        return updateCount;
1✔
2230
                } catch (SQLException ex) {
×
2231
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_UPDATE, ex);
×
2232
                }
2233
        }
2234

2235
        /**
2236
         * {@inheritDoc}
2237
         *
2238
         * @see jp.co.future.uroborosql.SqlAgent#updateAndReturn(java.lang.Object)
2239
         */
2240
        @Override
2241
        public <E> E updateAndReturn(final E entity) {
2242
                update(entity);
1✔
2243
                return entity;
1✔
2244
        }
2245

2246
        /**
2247
         * {@inheritDoc}
2248
         *
2249
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.Class)
2250
         */
2251
        @Override
2252
        public <E> SqlEntityUpdate<E> update(final Class<? extends E> entityType) {
2253
                var handler = this.getEntityHandler();
1✔
2254
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2255
                        throw new IllegalArgumentException("Entity type not supported");
×
2256
                }
2257

2258
                try {
2259
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2260

2261
                        var context = handler.createUpdateContext(this, metadata, entityType, false);
1✔
2262
                        context.setSqlKind(SqlKind.ENTITY_UPDATE);
1✔
2263

2264
                        return new SqlEntityUpdateImpl<>(this, handler, metadata, context, entityType);
1✔
2265
                } catch (SQLException ex) {
×
2266
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_UPDATE, ex);
×
2267
                }
2268
        }
2269

2270
        /**
2271
         * {@inheritDoc}
2272
         *
2273
         * @see jp.co.future.uroborosql.SqlAgent#merge(java.lang.Object)
2274
         */
2275
        @Override
2276
        public <E> int merge(final E entity) {
2277
                mergeAndReturn(entity);
1✔
2278
                return 1;
1✔
2279
        }
2280

2281
        /**
2282
         * {@inheritDoc}
2283
         *
2284
         * @see jp.co.future.uroborosql.SqlAgent#mergeAndReturn(java.lang.Object)
2285
         */
2286
        @Override
2287
        public <E> E mergeAndReturn(final E entity) {
2288
                return doMergeAndReturn(entity, false);
1✔
2289
        }
2290

2291
        /**
2292
         * {@inheritDoc}
2293
         *
2294
         * @see jp.co.future.uroborosql.SqlAgent#mergeWithLocking(java.lang.Object)
2295
         */
2296
        @Override
2297
        public <E> int mergeWithLocking(final E entity) {
2298
                mergeWithLockingAndReturn(entity);
1✔
2299
                return 1;
1✔
2300
        }
2301

2302
        /**
2303
         * {@inheritDoc}
2304
         *
2305
         * @see jp.co.future.uroborosql.SqlAgent#mergeWithLockingAndReturn(java.lang.Object)
2306
         */
2307
        @Override
2308
        public <E> E mergeWithLockingAndReturn(final E entity) {
2309
                return doMergeAndReturn(entity, true);
1✔
2310
        }
2311

2312
        /**
2313
         * エンティティのMERGE(INSERTまたはUPDATE)を実行し、MERGEしたエンティティを返却する.<br>
2314
         * locking引数が<code>true</code>の場合、マージの最初に発行するEntityの検索で悲観ロック(forUpdateNoWait)を行う
2315
         *
2316
         * @param <E> エンティティ型
2317
         * @param entity エンティティ
2318
         * @param locking エンティティの悲観ロックを行うかどうか
2319
         * @return MERGEしたエンティティ
2320
         *
2321
         */
2322
        @SuppressWarnings("unchecked")
2323
        private <E> E doMergeAndReturn(final E entity, final boolean locking) {
2324
                if (entity instanceof Stream) {
1✔
2325
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2326
                }
2327

2328
                var handler = this.getEntityHandler();
1✔
2329
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2330
                        throw new IllegalArgumentException("Entity type not supported.");
×
2331
                }
2332

2333
                var type = entity.getClass();
1✔
2334
                try {
2335
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
2336
                        var keyColumns = metadata.getKeyColumns();
1✔
2337

2338
                        if (keyColumns.isEmpty()) {
1✔
2339
                                throw new IllegalArgumentException("Entity has no keys.");
×
2340
                        }
2341

2342
                        var mappingColumns = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.UPDATE);
1✔
2343
                        var query = (SqlEntityQuery<E>) query(type);
1✔
2344
                        for (var column : keyColumns) {
1✔
2345
                                var camelName = column.getCamelColumnName();
1✔
2346
                                query.equal(camelName, mappingColumns.get(camelName).getValue(entity));
1✔
2347
                        }
1✔
2348
                        if (locking) {
1✔
2349
                                query.forUpdateNoWait();
1✔
2350
                        }
2351
                        return query.first()
1✔
2352
                                        .map(findEntity -> {
1✔
2353
                                                for (var mappingColumn : mappingColumns.values()) {
1✔
2354
                                                        if (!mappingColumn.isId() && !mappingColumn.isVersion()) {
1✔
2355
                                                                var value = mappingColumn.getValue(entity);
1✔
2356
                                                                if (value != null) {
1✔
2357
                                                                        mappingColumn.setValue(findEntity, value);
1✔
2358
                                                                }
2359
                                                        }
2360
                                                }
1✔
2361
                                                return updateAndReturn(findEntity);
1✔
2362
                                        }).orElseGet(() -> insertAndReturn(entity));
1✔
2363
                } catch (SQLException ex) {
×
2364
                        throw new EntitySqlRuntimeException(SqlKind.MERGE, ex);
×
2365
                }
2366
        }
2367

2368
        /**
2369
         * {@inheritDoc}
2370
         *
2371
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Object)
2372
         */
2373
        @Override
2374
        public <E> int delete(final E entity) {
2375
                if (entity instanceof Stream) {
1✔
2376
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2377
                }
2378

2379
                var handler = this.getEntityHandler();
1✔
2380
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2381
                        throw new IllegalArgumentException("Entity type not supported");
×
2382
                }
2383

2384
                try {
2385
                        var entityType = entity.getClass();
1✔
2386
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2387
                        var context = handler.createDeleteContext(this, metadata, entityType, true);
1✔
2388
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2389

2390
                        // EntityDelete実行前イベント発行
2391
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2392
                        if (eventListenerHolder.hasBeforeEntityDeleteListener()) {
1✔
2393
                                var eventObj = new BeforeEntityDeleteEvent(context, entity, entityType);
1✔
2394
                                for (var listener : eventListenerHolder.getBeforeEntityDeleteListeners()) {
1✔
2395
                                        listener.accept(eventObj);
1✔
2396
                                }
1✔
2397
                        }
2398
                        handler.setDeleteParams(context, entity);
1✔
2399

2400
                        var count = handler.doDelete(this, context, entity);
1✔
2401

2402
                        // EntityDelete実行後イベント発行
2403
                        if (eventListenerHolder.hasAfterEntityDeleteListener()) {
1✔
2404
                                var eventObj = new AfterEntityDeleteEvent(context, entity, entityType, count);
1✔
2405
                                for (var listener : eventListenerHolder.getAfterEntityDeleteListeners()) {
1✔
2406
                                        listener.accept(eventObj);
1✔
2407
                                }
1✔
2408
                                count = eventObj.getCount();
1✔
2409
                        }
2410

2411
                        return count;
1✔
2412
                } catch (SQLException ex) {
×
2413
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2414
                }
2415
        }
2416

2417
        /**
2418
         * {@inheritDoc}
2419
         *
2420
         * @see jp.co.future.uroborosql.SqlAgent#deleteAndReturn(java.lang.Object)
2421
         */
2422
        @Override
2423
        public <E> E deleteAndReturn(final E entity) {
2424
                delete(entity);
1✔
2425
                return entity;
1✔
2426
        }
2427

2428
        /**
2429
         * {@inheritDoc}
2430
         *
2431
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class, java.lang.Object[])
2432
         */
2433
        @Override
2434
        public <E> int delete(final Class<? extends E> entityType, final Object... keys) {
2435
                var handler = this.getEntityHandler();
1✔
2436
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2437
                        throw new IllegalArgumentException("Entity type not supported");
×
2438
                }
2439

2440
                try {
2441
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2442
                        TableMetadata.Column keyColumn = null;
1✔
2443
                        var keyColumns = metadata.getKeyColumns();
1✔
2444
                        if (keyColumns.size() == 1) {
1✔
2445
                                keyColumn = keyColumns.get(0);
1✔
2446
                        } else if (keyColumns.isEmpty()) {
1✔
2447
                                keyColumn = metadata.getColumns().get(0);
1✔
2448
                        } else {
2449
                                throw new IllegalArgumentException("Entity has multiple keys");
1✔
2450
                        }
2451
                        return delete(entityType).in(keyColumn.getCamelColumnName(), keys).count();
1✔
2452
                } catch (SQLException ex) {
×
2453
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2454
                }
2455
        }
2456

2457
        /**
2458
         * {@inheritDoc}
2459
         *
2460
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class)
2461
         */
2462
        @Override
2463
        public <E> SqlEntityDelete<E> delete(final Class<? extends E> entityType) {
2464
                var handler = this.getEntityHandler();
1✔
2465
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2466
                        throw new IllegalArgumentException("Entity type not supported");
×
2467
                }
2468

2469
                try {
2470
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2471
                        var context = handler.createDeleteContext(this, metadata, entityType, false);
1✔
2472
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2473
                        return new SqlEntityDeleteImpl<>(this, handler, metadata, context, entityType);
1✔
2474
                } catch (SQLException ex) {
×
2475
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2476
                }
2477
        }
2478

2479
        /**
2480
         * {@inheritDoc}
2481
         *
2482
         * @see jp.co.future.uroborosql.SqlAgent#truncate(java.lang.Class)
2483
         */
2484
        @Override
2485
        public <E> SqlAgent truncate(final Class<? extends E> entityType) {
2486
                var handler = this.getEntityHandler();
1✔
2487
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2488
                        throw new IllegalArgumentException("Entity type not supported");
×
2489
                }
2490
                try {
2491
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2492
                        var context = this.context().setSql("truncate table " + metadata.getTableIdentifier());
1✔
2493
                        context.setSqlKind(SqlKind.TRUNCATE);
1✔
2494
                        update(context);
1✔
2495
                        return this;
1✔
2496
                } catch (SQLException ex) {
1✔
2497
                        throw new EntitySqlRuntimeException(SqlKind.TRUNCATE, ex);
1✔
2498
                }
2499
        }
2500

2501
        /**
2502
         * 自動採番カラムを取得する. 合わせて、{@link ExecutionContext#setGeneratedKeyColumns(String[])} に自動採番カラムを設定する.
2503
         * <pre>
2504
         * 自動採番カラムの条件
2505
         * 1. {@link MappingColumn#isId()} == true 、もしくは{@link TableMetadata.Column#isAutoincrement()} == true であること
2506
         * 2. 1.に加え、エンティティの対象フィールド型がprimitive型、もしくはフィールドの値が<code>null</code>であること
2507
         * </pre>
2508
         *
2509
         * @param <E> entity
2510
         * @param context ExecutionContext
2511
         * @param mappingColumns EntityのMappingColumn配列
2512
         * @param metadata TableMetadata
2513
         * @param entity Entity
2514
         * @return 自動採番カラム配列
2515
         */
2516
        private <E> List<MappingColumn> getAutoGeneratedColumns(final ExecutionContext context,
2517
                        final MappingColumn[] mappingColumns,
2518
                        final TableMetadata metadata,
2519
                        final E entity) {
2520
                var autoGeneratedColumns = new ArrayList<MappingColumn>();
1✔
2521
                var autoGeneratedColumnNames = new ArrayList<String>();
1✔
2522

2523
                if (mappingColumns != null && mappingColumns.length > 0) {
1✔
2524
                        for (var column : metadata.getColumns()) {
1✔
2525
                                if (column.isAutoincrement()) {
1✔
2526
                                        for (var mappingColumn : mappingColumns) {
1✔
2527
                                                if (mappingColumn.getCamelName().equals(column.getCamelColumnName())) {
1✔
2528
                                                        // primitive型か、エンティティの対象フィールドがnullとなっている場合自動生成カラムとする
2529
                                                        if (mappingColumn.getJavaType().getRawType().isPrimitive()
1✔
2530
                                                                        || mappingColumn.getValue(entity) == null) {
1✔
2531
                                                                autoGeneratedColumns.add(mappingColumn);
1✔
2532
                                                                autoGeneratedColumnNames.add(column.getColumnName());
1✔
2533
                                                        }
2534
                                                        break;
2535
                                                }
2536
                                        }
2537
                                }
2538
                        }
1✔
2539
                        for (var mappingColumn : mappingColumns) {
1✔
2540
                                if (!mappingColumn.isId() || autoGeneratedColumns.contains(mappingColumn)) {
1✔
2541
                                        continue;
1✔
2542
                                }
2543
                                for (var column : metadata.getColumns()) {
1✔
2544
                                        if (mappingColumn.getCamelName().equals(column.getCamelColumnName())) {
1✔
2545
                                                // primitive型か、エンティティの対象フィールドがnullとなっている場合自動生成カラムとする
2546
                                                if (mappingColumn.getJavaType().getRawType().isPrimitive()
1✔
2547
                                                                || mappingColumn.getValue(entity) == null) {
1✔
2548
                                                        autoGeneratedColumns.add(mappingColumn);
1✔
2549
                                                        autoGeneratedColumnNames.add(column.getColumnName());
1✔
2550
                                                }
2551
                                                break;
2552
                                        }
2553
                                }
1✔
2554
                        }
2555
                }
2556

2557
                if (!autoGeneratedColumnNames.isEmpty()) {
1✔
2558
                        context.setGeneratedKeyColumns(
1✔
2559
                                        autoGeneratedColumnNames.toArray(new String[autoGeneratedColumnNames.size()]));
1✔
2560
                }
2561
                return autoGeneratedColumns;
1✔
2562
        }
2563

2564
        /**
2565
         * entityにID値を設定する
2566
         * @param entity Entity
2567
         * @param id ID値
2568
         * @param column 設定する対象のカラム
2569
         */
2570
        private void setEntityIdValue(final Object entity, final Object id, final MappingColumn column) {
2571
                var rawType = column.getJavaType().getRawType();
1✔
2572
                try {
2573
                        if (int.class.equals(rawType) || Integer.class.equals(rawType)) {
1✔
2574
                                if (id instanceof Number) {
1✔
2575
                                        column.setValue(entity, ((Number) id).intValue());
1✔
2576
                                } else {
2577
                                        throw new IllegalArgumentException();
1✔
2578
                                }
2579
                        } else if (long.class.equals(rawType) || Long.class.equals(rawType)) {
1✔
2580
                                if (id instanceof Number) {
1✔
2581
                                        column.setValue(entity, ((Number) id).longValue());
1✔
2582
                                } else {
2583
                                        throw new IllegalArgumentException();
1✔
2584
                                }
2585
                        } else if (BigInteger.class.equals(rawType)) {
1✔
2586
                                if (id instanceof BigInteger) {
1✔
2587
                                        column.setValue(entity, id);
×
2588
                                } else if (id instanceof BigDecimal) {
1✔
2589
                                        column.setValue(entity, ((BigDecimal) id).toBigInteger());
1✔
2590
                                } else if (id instanceof Number) {
1✔
2591
                                        column.setValue(entity, new BigInteger(((Number) id).toString()));
1✔
2592
                                } else {
2593
                                        throw new IllegalArgumentException();
1✔
2594
                                }
2595
                        } else if (BigDecimal.class.equals(rawType)) {
1✔
2596
                                if (id instanceof BigDecimal) {
1✔
2597
                                        column.setValue(entity, id);
1✔
2598
                                } else if (id instanceof BigInteger) {
1✔
2599
                                        column.setValue(entity, new BigDecimal((BigInteger) id));
×
2600
                                } else if (id instanceof Number) {
1✔
2601
                                        column.setValue(entity, new BigDecimal(((Number) id).toString()));
1✔
2602
                                } else {
2603
                                        throw new IllegalArgumentException();
1✔
2604
                                }
2605
                        } else if (String.class.equals(rawType)) {
1✔
2606
                                if (id instanceof String) {
1✔
2607
                                        column.setValue(entity, id);
×
2608
                                } else if (id instanceof BigDecimal) {
1✔
2609
                                        column.setValue(entity, ((BigDecimal) id).toPlainString());
1✔
2610
                                } else if (id instanceof Number) {
1✔
2611
                                        column.setValue(entity, ((Number) id).toString());
1✔
2612
                                } else {
2613
                                        column.setValue(entity, id.toString());
1✔
2614
                                }
2615
                        } else {
2616
                                try {
2617
                                        column.setValue(entity, id);
1✔
2618
                                } catch (UroborosqlRuntimeException ex) {
1✔
2619
                                        throw new UroborosqlRuntimeException(
1✔
2620
                                                        "Column is not correct as ID type. column=" + column.getCamelName() + ", type=" + rawType);
1✔
2621
                                }
1✔
2622
                        }
2623
                } catch (IllegalArgumentException ex) {
1✔
2624
                        throw new UroborosqlRuntimeException(
1✔
2625
                                        "Column type and ID type do not match. column=" + column.getCamelName() + ", type="
1✔
2626
                                                        + rawType
2627
                                                        + ", id=" + id + ", type=" + id.getClass().getSimpleName());
1✔
2628
                }
1✔
2629
        }
1✔
2630

2631
        /**
2632
         *
2633
         * {@inheritDoc}
2634
         *
2635
         * @see jp.co.future.uroborosql.SqlAgent#getSqlConfig()
2636
         */
2637
        @Override
2638
        public SqlConfig getSqlConfig() {
2639
                return this.sqlConfig;
1✔
2640
        }
2641

2642
        /**
2643
         *
2644
         * {@inheritDoc}
2645
         *
2646
         * @see jp.co.future.uroborosql.SqlAgent#context()
2647
         */
2648
        @Override
2649
        public ExecutionContext context() {
2650
                return sqlConfig.context();
1✔
2651
        }
2652

2653
        /**
2654
         * {@inheritDoc}
2655
         *
2656
         * @see jp.co.future.uroborosql.SqlAgent#getFetchSize()
2657
         */
2658
        @Override
2659
        public int getFetchSize() {
2660
                return fetchSize;
1✔
2661
        }
2662

2663
        /**
2664
         * {@inheritDoc}
2665
         *
2666
         * @see jp.co.future.uroborosql.SqlAgent#setFetchSize(int)
2667
         */
2668
        @Override
2669
        public SqlAgent setFetchSize(final int fetchSize) {
2670
                this.fetchSize = fetchSize;
1✔
2671
                return this;
1✔
2672
        }
2673

2674
        /**
2675
         * {@inheritDoc}
2676
         *
2677
         * @see jp.co.future.uroborosql.SqlAgent#getQueryTimeout()
2678
         */
2679
        @Override
2680
        public int getQueryTimeout() {
2681
                return queryTimeout;
1✔
2682
        }
2683

2684
        /**
2685
         * {@inheritDoc}
2686
         *
2687
         * @see jp.co.future.uroborosql.SqlAgent#setQueryTimeout(int)
2688
         */
2689
        @Override
2690
        public SqlAgent setQueryTimeout(final int queryTimeout) {
2691
                this.queryTimeout = queryTimeout;
1✔
2692
                return this;
1✔
2693
        }
2694

2695
        /**
2696
         * SQL実行をリトライするSQLエラーコードのリスト を取得します
2697
         *
2698
         * @return SQL実行をリトライするSQLエラーコードのリスト
2699
         */
2700
        public List<String> getSqlRetryCodes() {
2701
                return sqlRetryCodes;
1✔
2702
        }
2703

2704
        /**
2705
         * SQL実行をリトライするSQLエラーコードのリスト を設定します
2706
         *
2707
         * @param sqlRetryCodes SQL実行をリトライするSQLエラーコードのリスト
2708
         * @return SqlAgent
2709
         */
2710
        public SqlAgent setSqlRetryCodes(final List<String> sqlRetryCodes) {
2711
                this.sqlRetryCodes = sqlRetryCodes;
1✔
2712
                return this;
1✔
2713
        }
2714

2715
        /**
2716
         * 最大リトライ回数 を取得します
2717
         *
2718
         * @return 最大リトライ回数
2719
         */
2720
        public int getMaxRetryCount() {
2721
                return maxRetryCount;
1✔
2722
        }
2723

2724
        /**
2725
         * 最大リトライ回数 を設定します
2726
         *
2727
         * @param maxRetryCount 最大リトライ回数
2728
         * @return SqlAgent
2729
         */
2730
        public SqlAgent setMaxRetryCount(final int maxRetryCount) {
2731
                this.maxRetryCount = maxRetryCount;
1✔
2732
                return this;
1✔
2733
        }
2734

2735
        /**
2736
         * リトライタイムアウト時間(ms) を取得します
2737
         *
2738
         * @return リトライタイムアウト時間(ms)
2739
         */
2740
        public int getRetryWaitTime() {
2741
                return retryWaitTime;
1✔
2742
        }
2743

2744
        /**
2745
         * リトライタイムアウト時間(ms) を設定します
2746
         *
2747
         * @param retryWaitTime リトライタイムアウト時間(ms)
2748
         * @return SqlAgent
2749
         */
2750
        public SqlAgent setRetryWaitTime(final int retryWaitTime) {
2751
                this.retryWaitTime = retryWaitTime;
1✔
2752
                return this;
1✔
2753
        }
2754

2755
        /**
2756
         *
2757
         * {@inheritDoc}
2758
         *
2759
         * @see jp.co.future.uroborosql.SqlAgent#getMapKeyCaseFormat()
2760
         */
2761
        @Override
2762
        public CaseFormat getMapKeyCaseFormat() {
2763
                return mapKeyCaseFormat;
1✔
2764
        }
2765

2766
        /**
2767
         *
2768
         * {@inheritDoc}
2769
         *
2770
         * @see jp.co.future.uroborosql.SqlAgent#setMapKeyCaseFormat(jp.co.future.uroborosql.utils.CaseFormat)
2771
         */
2772
        @Override
2773
        public SqlAgent setMapKeyCaseFormat(final CaseFormat mapKeyCaseFormat) {
2774
                this.mapKeyCaseFormat = mapKeyCaseFormat;
1✔
2775
                return this;
1✔
2776
        }
2777

2778
        /**
2779
         *
2780
         * {@inheritDoc}
2781
         *
2782
         * @see jp.co.future.uroborosql.SqlAgent#getInsertsType()
2783
         */
2784
        @Override
2785
        public InsertsType getInsertsType() {
2786
                return this.insertsType;
1✔
2787
        }
2788

2789
        /**
2790
         *
2791
         * {@inheritDoc}
2792
         *
2793
         * @see jp.co.future.uroborosql.SqlAgent#setInsertsType(jp.co.future.uroborosql.enums.InsertsType)
2794
         */
2795
        @Override
2796
        public SqlAgent setInsertsType(final InsertsType defaultInsertsType) {
2797
                this.insertsType = defaultInsertsType;
1✔
2798
                return this;
1✔
2799
        }
2800

2801
        /**
2802
         * SQLリソース管理クラスを取得します。
2803
         *
2804
         * @return SQLリソース管理クラス
2805
         */
2806
        private SqlResourceManager getSqlResourceManager() {
2807
                return sqlConfig.getSqlResourceManager();
1✔
2808
        }
2809

2810
        /**
2811
         * ORM処理クラス を取得します。
2812
         *
2813
         * @return ORM処理クラス
2814
         */
2815
        @SuppressWarnings("unchecked")
2816
        private <E> EntityHandler<E> getEntityHandler() {
2817
                return (EntityHandler<E>) sqlConfig.getEntityHandler();
1✔
2818
        }
2819

2820
        /**
2821
         * Dialect を取得します。
2822
         *
2823
         * @return Dialect
2824
         */
2825
        private Dialect getDialect() {
2826
                return sqlConfig.getDialect();
1✔
2827
        }
2828

2829
        /**
2830
         * ステートメント初期化。
2831
         *
2832
         * @param executionContext ExecutionContext
2833
         * @return PreparedStatement
2834
         * @throws SQLException SQL例外
2835
         */
2836
        private PreparedStatement getPreparedStatement(final ExecutionContext executionContext) throws SQLException {
2837
                var stmt = ((LocalTransactionManager) transactionManager).getPreparedStatement(executionContext);
1✔
2838
                // プロパティ設定
2839
                applyProperties(stmt);
1✔
2840
                return stmt;
1✔
2841
        }
2842

2843
        /**
2844
         * Callableステートメント初期化
2845
         *
2846
         * @param executionContext ExecutionContext
2847
         * @return CallableStatement
2848
         * @throws SQLException SQL例外
2849
         */
2850
        private CallableStatement getCallableStatement(final ExecutionContext executionContext) throws SQLException {
2851
                var stmt = ((LocalTransactionManager) transactionManager).getCallableStatement(executionContext);
1✔
2852
                // プロパティ設定
2853
                applyProperties(stmt);
1✔
2854
                return stmt;
1✔
2855
        }
2856

2857
        /**
2858
         * フェッチサイズとクエリタイムアウトをPreparedStatementに設定する
2859
         *
2860
         * @param preparedStatement PreparedStatement
2861
         * @throws SQLException SQL例外
2862
         */
2863
        private void applyProperties(final PreparedStatement preparedStatement) throws SQLException {
2864
                // フェッチサイズ指定
2865
                if (getFetchSize() >= 0 && !(preparedStatement instanceof CallableStatement)) {
1✔
2866
                        preparedStatement.setFetchSize(getFetchSize());
1✔
2867
                }
2868

2869
                // クエリタイムアウト指定
2870
                if (getQueryTimeout() >= 0) {
1✔
2871
                        preparedStatement.setQueryTimeout(getQueryTimeout());
1✔
2872
                }
2873
        }
1✔
2874

2875
        /**
2876
         * 経過時間を計算し、HH:mm:ss.SSSSSSにフォーマットする.
2877
         *
2878
         * @param start 開始時間
2879
         * @param end 終了時間
2880
         * @return フォーマットした経過時間
2881
         */
2882
        private static String formatElapsedTime(final Instant start, final Instant end) {
2883
                return ELAPSED_TIME_FORMAT.format(LocalTime.MIDNIGHT.plus(Duration.between(start != null ? start : end, end)));
×
2884
        }
2885

2886
        /**
2887
         * ResultSetをStreamで扱うためのSpliterator
2888
         *
2889
         * @author H.Sugimoto
2890
         *
2891
         * @param <T> ResultSetの1行を変換した型
2892
         */
2893
        private static final class ResultSetSpliterator<T> extends Spliterators.AbstractSpliterator<T>
2894
                        implements ServiceLoggingSupport {
2895
                private final ResultSetConverter<T> converter;
2896
                private final ResultSet rs;
2897
                private boolean finished = false;
1✔
2898

2899
                private ResultSetSpliterator(final ResultSet rs, final ResultSetConverter<T> converter) {
2900
                        super(Long.MAX_VALUE, Spliterator.ORDERED);
1✔
2901
                        this.rs = rs;
1✔
2902
                        this.converter = converter;
1✔
2903
                }
1✔
2904

2905
                @Override
2906
                public boolean tryAdvance(final Consumer<? super T> action) {
2907
                        try {
2908
                                if (finished || !rs.next()) {
1✔
2909
                                        if (!rs.isClosed()) {
1✔
2910
                                                rs.close();
1✔
2911
                                        }
2912
                                        finished = true;
1✔
2913
                                        return false;
1✔
2914
                                }
2915
                                action.accept(converter.createRecord(rs));
1✔
2916
                                return true;
1✔
2917
                        } catch (RuntimeException | Error ex) {
1✔
2918
                                try {
2919
                                        if (rs != null && !rs.isClosed()) {
1✔
2920
                                                rs.close();
1✔
2921
                                        }
2922
                                } catch (SQLException ex2) {
×
2923
                                        errorWith(LOG)
×
2924
                                                        .setMessage(ex2.getMessage())
×
2925
                                                        .setCause(ex2)
×
2926
                                                        .log();
×
2927
                                }
1✔
2928
                                throw ex;
1✔
2929
                        } catch (SQLException ex) {
×
2930
                                try {
2931
                                        if (rs != null && !rs.isClosed()) {
×
2932
                                                rs.close();
×
2933
                                        }
2934
                                } catch (SQLException ex2) {
×
2935
                                        errorWith(LOG)
×
2936
                                                        .setMessage(ex2.getMessage())
×
2937
                                                        .setCause(ex2)
×
2938
                                                        .log();
×
2939
                                }
×
2940
                                throw new UroborosqlSQLException(ex);
×
2941
                        }
2942
                }
2943
        }
2944

2945
        /**
2946
         * ResultSetのラッパークラス。ResultSetのクローズに合わせてStatementもクローズする。
2947
         *
2948
         * @author H.Sugimoto
2949
         * @version 0.5.0
2950
         */
2951
        private static class InnerResultSet extends AbstractResultSetWrapper {
2952
                /** 同期してクローズするStatement */
2953
                private final Statement stmt;
2954

2955
                /**
2956
                 * コンストラクタ
2957
                 *
2958
                 * @param wrapped 元となるResultSet
2959
                 * @param stmt Statement
2960
                 */
2961
                InnerResultSet(final ResultSet wrapped, final Statement stmt) {
2962
                        super(wrapped);
1✔
2963
                        this.stmt = stmt;
1✔
2964
                }
1✔
2965

2966
                /**
2967
                 * {@inheritDoc}
2968
                 *
2969
                 * @see jp.co.future.uroborosql.AbstractResultSetWrapper#close()
2970
                 */
2971
                @Override
2972
                public void close() throws SQLException {
2973
                        try {
2974
                                super.close();
1✔
2975
                        } finally {
2976
                                try {
2977
                                        if (stmt != null && !stmt.isClosed()) {
1✔
2978
                                                stmt.close();
1✔
2979
                                        }
2980
                                } catch (SQLException ex) {
×
2981
                                        // do nothing
2982
                                }
1✔
2983
                        }
2984
                }
1✔
2985
        }
2986
}
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