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

future-architect / uroborosql / #746

15 Jul 2024 04:58PM UTC coverage: 91.311% (+0.3%) from 90.988%
#746

Pull #311

HidekiSugimoto189
rename event name and add SystemColumnTest
Pull Request #311: refactoring api

634 of 646 new or added lines in 29 files covered. (98.14%)

5 existing lines in 1 file now uncovered.

8743 of 9575 relevant lines covered (91.31%)

0.91 hits per line

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

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

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

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

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

99
/**
100
 * SQL実行用クラス。
101
 *
102
 * @author H.Sugimoto
103
 */
104
public class SqlAgentImpl implements SqlAgent {
105
        /** ロガー */
106
        private static final Logger LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.log");
1✔
107

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

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

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

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

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

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

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

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

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

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

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

141
        /** SQL設定管理クラス */
142
        private final SqlConfig sqlConfig;
143

144
        /** トランザクション管理機能 */
145
        private final TransactionManager transactionManager;
146

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

150
        /** クエリータイムアウト制限値 */
151
        private int queryTimeout = -1;
1✔
152

153
        /** フェッチサイズ */
154
        private int fetchSize = -1;
1✔
155

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

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

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

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

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

171
        /** {@link InsertsType} */
172
        private InsertsType insertsType = InsertsType.BATCH;
1✔
173

174
        static {
175
                // SQLカバレッジ取得用のクラス名を設定する。指定がない場合、またはfalseが指定された場合はカバレッジを収集しない。
176
                // クラス名が指定されている場合はそのクラス名を指定
177
                var sqlCoverageClassName = System.getProperty(KEY_SQL_COVERAGE);
1✔
178
                if (sqlCoverageClassName == null) {
1✔
179
                        COVERAGE_LOG.info("system property - uroborosql.sql.coverage not set. sql coverage turned off.");
×
180
                } else if (Boolean.FALSE.toString().equalsIgnoreCase(sqlCoverageClassName)) {
1✔
181
                        sqlCoverageClassName = null;
×
182
                        COVERAGE_LOG.info("system property - uroborosql.sql.coverage is set to false. sql coverage turned off.");
×
183
                } else if (Boolean.TRUE.toString().equalsIgnoreCase(sqlCoverageClassName)) {
1✔
184
                        // trueの場合は、デフォルト値を設定
185
                        sqlCoverageClassName = "jp.co.future.uroborosql.coverage.CoberturaCoverageHandler";
1✔
186
                        COVERAGE_LOG.info("system property - uroborosql.sql.coverage is set to true. sql coverage turned on.");
1✔
187
                }
188

189
                CoverageHandler handler = null;
1✔
190
                if (sqlCoverageClassName != null) {
1✔
191
                        try {
192
                                handler = (CoverageHandler) Class.forName(sqlCoverageClassName, true,
1✔
193
                                                Thread.currentThread().getContextClassLoader()).getConstructor().newInstance();
1✔
194
                                COVERAGE_LOG.info("CoverageHandler : {}", sqlCoverageClassName);
1✔
195
                        } catch (Exception ex) {
×
196
                                COVERAGE_LOG.warn("Failed to instantiate CoverageHandler class. Class:{}, Cause:{}",
×
197
                                                sqlCoverageClassName,
198
                                                ex.getMessage());
×
199
                                COVERAGE_LOG.info("Turn off sql coverage due to failure to instantiate CoverageHandler class.");
×
200
                        }
1✔
201
                }
202

203
                if (handler != null) {
1✔
204
                        COVERAGE_HANDLER_REF.set(handler);
1✔
205
                }
206
        }
1✔
207

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

530
        /**
531
         * {@inheritDoc}
532
         *
533
         * @see jp.co.future.uroborosql.SqlAgent#batch(java.lang.String)
534
         */
535
        @Override
536
        public SqlBatch batch(final String sqlName) {
537
                if ("".equals(sqlName)) {
1✔
538
                        throw new IllegalArgumentException("sqlName is required.");
1✔
539
                }
540
                return new SqlBatchImpl(this, context().setSqlName(sqlName));
1✔
541
        }
542

543
        /**
544
         * {@inheritDoc}
545
         *
546
         * @see jp.co.future.uroborosql.SqlAgent#batch(java.util.function.Supplier)
547
         */
548
        @Override
549
        public SqlBatch batch(final Supplier<String> supplier) {
550
                return this.batch(supplier.get());
1✔
551
        }
552

553
        /**
554
         * {@inheritDoc}
555
         *
556
         * @see jp.co.future.uroborosql.SqlAgent#batchWith(java.lang.String)
557
         */
558
        @Override
559
        public SqlBatch batchWith(final String sql) {
560
                if (sql == null || "".equals(sql)) {
1✔
561
                        throw new IllegalArgumentException("sql is required.");
1✔
562
                }
563
                return new SqlBatchImpl(this, context().setSql(sql));
1✔
564
        }
565

566
        /**
567
         * {@inheritDoc}
568
         *
569
         * @see jp.co.future.uroborosql.SqlAgent#proc(java.lang.String)
570
         */
571
        @Override
572
        public Procedure proc(final String sqlName) {
573
                if ("".equals(sqlName)) {
1✔
574
                        throw new IllegalArgumentException("sqlName is required.");
1✔
575
                }
576
                return new ProcedureImpl(this, context().setSqlName(sqlName));
1✔
577
        }
578

579
        /**
580
         * {@inheritDoc}
581
         *
582
         * @see jp.co.future.uroborosql.SqlAgent#proc(java.util.function.Supplier)
583
         */
584
        @Override
585
        public Procedure proc(final Supplier<String> supplier) {
586
                return this.proc(supplier.get());
1✔
587
        }
588

589
        /**
590
         * {@inheritDoc}
591
         *
592
         * @see jp.co.future.uroborosql.SqlAgent#procWith(java.lang.String)
593
         */
594
        @Override
595
        public Procedure procWith(final String sql) {
596
                if (sql == null || "".equals(sql)) {
1✔
597
                        throw new IllegalArgumentException("sql is required.");
1✔
598
                }
599
                return new ProcedureImpl(this, context().setSql(sql));
1✔
600
        }
601

602
        /**
603
         * {@inheritDoc}
604
         *
605
         * @see jp.co.future.uroborosql.SqlAgent#query(java.lang.Class)
606
         */
607
        @Override
608
        public <E> SqlEntityQuery<E> query(final Class<? extends E> entityType) {
609
                var handler = this.getEntityHandler();
1✔
610
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
611
                        throw new IllegalArgumentException("Entity type not supported");
×
612
                }
613
                try {
614
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
615
                        var context = handler.createSelectContext(this, metadata, entityType, false);
1✔
616
                        context.setSqlKind(SqlKind.ENTITY_SELECT);
1✔
617
                        return new SqlEntityQueryImpl<>(this, handler, metadata, context, entityType);
1✔
618
                } catch (SQLException ex) {
×
619
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_SELECT, ex);
×
620
                }
621
        }
622

623
        /**
624
         * {@inheritDoc}
625
         *
626
         * @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)
627
         */
628
        @Override
629
        public <E> int inserts(final Class<E> entityType,
630
                        final Stream<E> entities,
631
                        final InsertsCondition<? super E> condition,
632
                        final InsertsType insertsType) {
633
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
634
                        return bulkInsert(entityType, entities, condition, null);
1✔
635
                } else {
636
                        return batchInsert(entityType, entities, condition, null);
1✔
637
                }
638
        }
639

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

651
        /**
652
         * {@inheritDoc}
653
         *
654
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
655
         */
656
        @Override
657
        public <E> int inserts(final Class<E> entityType,
658
                        final Stream<E> entities,
659
                        final InsertsCondition<? super E> condition) {
660
                return inserts(entityType, entities, condition, getInsertsType());
1✔
661
        }
662

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

675
        /**
676
         * {@inheritDoc}
677
         *
678
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
679
         */
680
        @Override
681
        public <E> int inserts(final Stream<E> entities,
682
                        final InsertsCondition<? super E> condition,
683
                        final InsertsType insertsType) {
684
                var iterator = entities.iterator();
1✔
685
                if (!iterator.hasNext()) {
1✔
686
                        return 0;
1✔
687
                }
688
                var firstEntity = iterator.next();
1✔
689
                @SuppressWarnings("unchecked")
690
                var type = (Class<E>) firstEntity.getClass();
1✔
691
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
692
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
693
                return inserts(type, stream, condition, insertsType);
1✔
694
        }
695

696
        /**
697
         * {@inheritDoc}
698
         *
699
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream)
700
         */
701
        @Override
702
        public <E> int inserts(final Stream<E> entities) {
703
                return inserts(entities, getInsertsCondition(getInsertsType()));
1✔
704
        }
705

706
        /**
707
         * {@inheritDoc}
708
         *
709
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
710
         */
711
        @Override
712
        public <E> int inserts(final Stream<E> entities,
713
                        final InsertsCondition<? super E> condition) {
714
                return inserts(entities, condition, getInsertsType());
1✔
715
        }
716

717
        /**
718
         * {@inheritDoc}
719
         *
720
         * @see jp.co.future.uroborosql.SqlAgent#inserts(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
721
         */
722
        @Override
723
        public <E> int inserts(final Stream<E> entities,
724
                        final InsertsType insertsType) {
725
                return inserts(entities, getInsertsCondition(insertsType), insertsType);
1✔
726
        }
727

728
        /**
729
         * {@inheritDoc}
730
         *
731
         * @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)
732
         */
733
        @Override
734
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
735
                        final Stream<E> entities,
736
                        final InsertsCondition<? super E> condition,
737
                        final InsertsType insertsType) {
738
                var insertedEntities = new ArrayList<E>();
1✔
739
                if (insertsType == InsertsType.BULK && getDialect().supportsBulkInsert()) {
1✔
740
                        bulkInsert(entityType, entities, condition, insertedEntities);
1✔
741
                } else {
742
                        batchInsert(entityType, entities, condition, insertedEntities);
1✔
743
                }
744
                return insertedEntities.stream();
1✔
745
        }
746

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

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

770
        /**
771
         * {@inheritDoc}
772
         *
773
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
774
         */
775
        @Override
776
        public <E> Stream<E> insertsAndReturn(final Class<E> entityType,
777
                        final Stream<E> entities,
778
                        final InsertsType insertsType) {
779
                return insertsAndReturn(entityType, entities, getInsertsCondition(insertsType), insertsType);
1✔
780
        }
781

782
        /**
783
         * {@inheritDoc}
784
         *
785
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition, jp.co.future.uroborosql.enums.InsertsType)
786
         */
787
        @Override
788
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
789
                        final InsertsCondition<? super E> condition,
790
                        final InsertsType insertsType) {
791
                var iterator = entities.iterator();
1✔
792
                if (!iterator.hasNext()) {
1✔
793
                        return Stream.empty();
1✔
794
                }
795
                var firstEntity = iterator.next();
1✔
796
                @SuppressWarnings("unchecked")
797
                var type = (Class<E>) firstEntity.getClass();
1✔
798
                var stream = Stream.concat(Stream.of(firstEntity),
1✔
799
                                StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false));
1✔
800
                return insertsAndReturn(type, stream, condition, insertsType);
1✔
801
        }
802

803
        /**
804
         * {@inheritDoc}
805
         *
806
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.InsertsCondition)
807
         */
808
        @Override
809
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
810
                        final InsertsCondition<? super E> condition) {
811
                return insertsAndReturn(entities, condition, getInsertsType());
1✔
812
        }
813

814
        /**
815
         * {@inheritDoc}
816
         *
817
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream)
818
         */
819
        @Override
820
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities) {
821
                return insertsAndReturn(entities, getInsertsCondition(getInsertsType()));
1✔
822
        }
823

824
        /**
825
         * {@inheritDoc}
826
         *
827
         * @see jp.co.future.uroborosql.SqlAgent#insertsAndReturn(java.util.stream.Stream, jp.co.future.uroborosql.enums.InsertsType)
828
         */
829
        @Override
830
        public <E> Stream<E> insertsAndReturn(final Stream<E> entities,
831
                        final InsertsType insertsType) {
832
                return insertsAndReturn(entities, getInsertsCondition(insertsType), insertsType);
1✔
833
        }
834

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

847
        /**
848
         * {@inheritDoc}
849
         *
850
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream, jp.co.future.uroborosql.SqlAgent.UpdatesCondition)
851
         */
852
        @Override
853
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
854
                        final Stream<E> entities,
855
                        final UpdatesCondition<? super E> condition) {
856
                var updatedEntities = new ArrayList<E>();
1✔
857
                batchUpdate(entityType, entities, condition, updatedEntities);
1✔
858
                return updatedEntities.stream();
1✔
859
        }
860

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

872
        /**
873
         * {@inheritDoc}
874
         *
875
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.lang.Class, java.util.stream.Stream)
876
         */
877
        @Override
878
        public <E> Stream<E> updatesAndReturn(final Class<E> entityType,
879
                        final Stream<E> entities) {
880
                return updatesAndReturn(entityType, entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
881
        }
882

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

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

923
        /**
924
         * {@inheritDoc}
925
         *
926
         * @see jp.co.future.uroborosql.SqlAgent#updates(java.util.stream.Stream)
927
         */
928
        @Override
929
        public <E> int updates(final Stream<E> entities) {
930
                return updates(entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
931
        }
932

933
        /**
934
         * {@inheritDoc}
935
         *
936
         * @see jp.co.future.uroborosql.SqlAgent#updatesAndReturn(java.util.stream.Stream)
937
         */
938
        @Override
939
        public <E> Stream<E> updatesAndReturn(final Stream<E> entities) {
940
                return updatesAndReturn(entities, DEFAULT_UPDATES_WHEN_CONDITION);
1✔
941
        }
942

943
        private <E> InsertsCondition<? super E> getInsertsCondition(final InsertsType insertsType) {
944
                return InsertsType.BATCH.equals(insertsType)
1✔
945
                                ? DEFAULT_BATCH_INSERTS_WHEN_CONDITION
1✔
946
                                : DEFAULT_BULK_INSERTS_WHEN_CONDITION;
1✔
947
        }
948

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

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

972
                        var count = 0;
1✔
973
                        var entityList = new ArrayList<E>();
1✔
974
                        var isFirst = true;
1✔
975
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
976
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
977
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
978
                        var hasBeforeEntityBatchInsertListener = eventListenerHolder.hasBeforeEntityBatchInsertListener();
1✔
979
                        var eventObj = hasBeforeEntityBatchInsertListener
1✔
980
                                        ? new BeforeEntityBatchInsertEvent(context, null, entityType)
1✔
981
                                        : null;
1✔
982
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
983
                                var entity = iterator.next();
1✔
984

985
                                if (!entityType.isInstance(entity)) {
1✔
986
                                        throw new IllegalArgumentException("Entity types do not match");
1✔
987
                                }
988

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

994
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
995
                                        var excludeColumns = autoGeneratedColumns;
1✔
996
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
997
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
998
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
999
                                                                        && col.getValue(entity) != null)
1✔
1000
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
1001
                                }
1002

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

1008
                                // EntityBatchInsert実行前イベント発行
1009
                                if (hasBeforeEntityBatchInsertListener) {
1✔
1010
                                        // BatchInsertでは大量のEntityが処理されるため、実行前イベントでは都度Eventインスタンスを生成せず、1つのイベントインスタンスを使いまわす実装とする(性能対応)
1011
                                        eventObj.setEntity(entity);
1✔
1012
                                        for (var listener : eventListenerHolder.getBeforeEntityBatchInsertListeners()) {
1✔
1013
                                                listener.accept(eventObj);
1✔
1014
                                        }
1✔
1015
                                }
1016
                                handler.setInsertParams(context, entity);
1✔
1017

1018
                                context.addBatch();
1✔
1019
                                // SQLのID項目IF分岐判定をtrueにするためにaddBatch()の後に保持しておいたID項目をcontextにバインドする
1020
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
1021
                                        context.paramMap(nonNullObjectIdFlags);
1✔
1022
                                }
1023
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
1024
                                        count += Arrays
1✔
1025
                                                        .stream(doBatchInsert(context, handler, entityList, entityType, autoGeneratedColumns))
1✔
1026
                                                        .sum();
1✔
1027
                                        entityList.clear();
1✔
1028
                                }
1029
                        }
1✔
1030
                        return count + (context.batchCount() != 0
1✔
1031
                                        ? Arrays.stream(doBatchInsert(context, handler, entityList, entityType, autoGeneratedColumns)).sum()
1✔
1032
                                        : 0);
1✔
1033
                } catch (SQLException ex) {
×
1034
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_BATCH_INSERT, ex);
×
1035
                }
1036
        }
1037

1038
        private <E> int[] doBatchInsert(final ExecutionContext context,
1039
                        final EntityHandler<E> handler,
1040
                        final List<E> entityList,
1041
                        final Class<E> entityType,
1042
                        final List<MappingColumn> autoGeneratedColumns)
1043
                        throws SQLException {
1044
                var counts = handler.doBatchInsert(this, context);
1✔
1045

1046
                // EntityBatchInsert実行後イベント発行
1047
                var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1048
                if (eventListenerHolder.hasAfterEntityBatchInsertListener()) {
1✔
1049
                        var eventObj = new AfterEntityBatchInsertEvent(context, entityList, entityType, counts);
1✔
1050
                        for (var listener : eventListenerHolder.getAfterEntityBatchInsertListeners()) {
1✔
1051
                                listener.accept(eventObj);
1✔
1052
                        }
1✔
1053
                        counts = eventObj.getCounts();
1✔
1054
                }
1055

1056
                if (!autoGeneratedColumns.isEmpty()) {
1✔
1057
                        var ids = context.getGeneratedKeyValues();
1✔
1058
                        var idx = 0;
1✔
1059
                        for (E ent : entityList) {
1✔
1060
                                for (var col : autoGeneratedColumns) {
1✔
1061
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
1062
                                }
1✔
1063
                        }
1✔
1064
                }
1065

1066
                return counts;
1✔
1067
        }
1068

1069
        /**
1070
         * 複数エンティティのINSERTをバッチ実行
1071
         *
1072
         * @param <E> エンティティの型
1073
         * @param entityType エンティティの型
1074
         * @param entities エンティティ
1075
         * @param condition 一括INSERT用のフレームの判定条件
1076
         * @param insertedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
1077
         * @return SQL実行結果
1078
         */
1079
        @SuppressWarnings("deprecation")
1080
        private <E> int bulkInsert(final Class<E> entityType, final Stream<E> entities,
1081
                        final InsertsCondition<? super E> condition, final List<E> insertedEntities) {
1082
                EntityHandler<E> handler = this.getEntityHandler();
1✔
1083
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1084
                        throw new IllegalArgumentException("Entity type not supported");
×
1085
                }
1086

1087
                try {
1088
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1089
                        var context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
1090
                        context.setSqlKind(SqlKind.ENTITY_BULK_INSERT);
1✔
1091

1092
                        var frameCount = 0;
1✔
1093
                        var count = 0;
1✔
1094
                        var isFirst = true;
1✔
1095
                        List<MappingColumn> autoGeneratedColumns = List.of();
1✔
1096
                        Map<String, Object> nonNullObjectIdFlags = null;
1✔
1097
                        var entityList = new ArrayList<E>();
1✔
1098
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1099
                        var hasBeforeEntityBulkInsertListener = eventListenerHolder.hasBeforeEntityBulkInsertListener();
1✔
1100
                        var eventObj = hasBeforeEntityBulkInsertListener
1✔
1101
                                        ? new BeforeEntityBulkInsertEvent(context, null, entityType, frameCount)
1✔
1102
                                        : null;
1✔
1103
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1104
                                var entity = iterator.next();
1✔
1105

1106
                                if (!entityType.isInstance(entity)) {
1✔
1107
                                        throw new IllegalArgumentException("Entity types do not match");
×
1108
                                }
1109

1110
                                if (isFirst) {
1✔
1111
                                        isFirst = false;
1✔
1112
                                        var mappingColumns = MappingUtils.getMappingColumns(metadata.getSchema(), entityType);
1✔
1113
                                        autoGeneratedColumns = getAutoGeneratedColumns(context, mappingColumns, metadata, entity);
1✔
1114

1115
                                        // SQLのID項目IF分岐判定をtrueにするために値が設定されているID項目を保持しておく
1116
                                        var excludeColumns = autoGeneratedColumns;
1✔
1117
                                        // indexなしのid値がcontextにバインドされないため、値としてkey=trueを退避しておく
1118
                                        nonNullObjectIdFlags = Arrays.stream(mappingColumns)
1✔
1119
                                                        .filter(col -> !excludeColumns.contains(col)
1✔
1120
                                                                        && !col.getJavaType().getRawType().isPrimitive()
1✔
1121
                                                                        && col.getValue(entity) != null)
1✔
1122
                                                        .collect(Collectors.toMap(MappingColumn::getCamelName, col -> true));
1✔
1123
                                }
1124
                                // 退避しておいたid値をこのタイミングで設定する
1125
                                if (nonNullObjectIdFlags != null && !nonNullObjectIdFlags.isEmpty()) {
1✔
1126
                                        context.paramMap(nonNullObjectIdFlags);
1✔
1127
                                }
1128

1129
                                entityList.add(entity);
1✔
1130
                                if (insertedEntities != null) {
1✔
1131
                                        insertedEntities.add(entity);
1✔
1132
                                }
1133

1134
                                // EntityBulkInsert実行前イベント発行
1135
                                if (hasBeforeEntityBulkInsertListener) {
1✔
1136
                                        // BulkInsertでは大量のEntityが処理されるため、実行前イベントでは都度Eventインスタンスを生成せず、1つのイベントインスタンスを使いまわす実装とする(性能対応)
1137
                                        eventObj.setEntity(entity);
1✔
1138
                                        eventObj.setFrameCount(frameCount);
1✔
1139
                                        for (var listener : eventListenerHolder.getBeforeEntityBulkInsertListeners()) {
1✔
1140
                                                listener.accept(eventObj);
1✔
1141
                                        }
1✔
1142
                                }
1143
                                handler.setBulkInsertParams(context, entity, frameCount);
1✔
1144

1145
                                frameCount++;
1✔
1146

1147
                                if (condition.test(context, frameCount, entity)) {
1✔
1148
                                        count += doBulkInsert(context, handler, entityList, entityType, metadata, autoGeneratedColumns,
1✔
1149
                                                        frameCount);
1150
                                        frameCount = 0;
1✔
1151
                                        entityList.clear();
1✔
1152

1153
                                        // 新しいExecutionContextを作成する前にgeneratedKeyColumnsを退避しておく
1154
                                        var generatedKeyColumns = context.getGeneratedKeyColumns();
1✔
1155
                                        context = handler.createBulkInsertContext(this, metadata, entityType);
1✔
1156
                                        context.setSqlKind(SqlKind.ENTITY_BULK_INSERT);
1✔
1157
                                        // 実行結果から生成されたIDを取得できるようにPreparedStatementにIDカラムを渡す
1158
                                        context.setGeneratedKeyColumns(generatedKeyColumns);
1✔
1159
                                }
1160
                        }
1✔
1161
                        return count + (frameCount > 0
1✔
1162
                                        ? doBulkInsert(context, handler, entityList, entityType, metadata, autoGeneratedColumns, frameCount)
1✔
1163
                                        : 0);
1✔
1164

1165
                } catch (SQLException ex) {
×
1166
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_BULK_INSERT, ex);
×
1167
                }
1168
        }
1169

1170
        private <E> int doBulkInsert(final ExecutionContext context,
1171
                        final EntityHandler<E> handler,
1172
                        final List<E> entityList,
1173
                        final Class<E> entityType,
1174
                        final TableMetadata metadata,
1175
                        final List<MappingColumn> autoGeneratedColumns,
1176
                        final int frameCount)
1177
                        throws SQLException {
1178
                var count = handler.doBulkInsert(this,
1✔
1179
                                handler.setupSqlBulkInsertContext(this, context, metadata, entityType, entityList.size()));
1✔
1180

1181
                // EntityBulkInsert実行前イベント発行
1182
                var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1183
                if (eventListenerHolder.hasAfterEntityBulkInsertListener()) {
1✔
1184
                        var eventObj = new AfterEntityBulkInsertEvent(context, entityList, entityType, frameCount, count);
1✔
1185
                        for (var listener : eventListenerHolder.getAfterEntityBulkInsertListeners()) {
1✔
1186
                                listener.accept(eventObj);
1✔
1187
                        }
1✔
1188
                        count = eventObj.getCount();
1✔
1189
                }
1190

1191
                if (!autoGeneratedColumns.isEmpty()) {
1✔
1192
                        var ids = context.getGeneratedKeyValues();
1✔
1193
                        var idx = 0;
1✔
1194
                        for (E ent : entityList) {
1✔
1195
                                for (var col : autoGeneratedColumns) {
1✔
1196
                                        setEntityIdValue(ent, ids[idx++], col);
1✔
1197
                                }
1✔
1198
                        }
1✔
1199
                }
1200

1201
                return count;
1✔
1202
        }
1203

1204
        /**
1205
         * 複数エンティティのBATCH UPDATEを実行
1206
         *
1207
         * @param <E> エンティティの型
1208
         * @param entityType エンティティの型
1209
         * @param entities エンティティ
1210
         * @param condition 一括更新用のフレームの判定条件
1211
         * @param updatedEntities INSERTしたEntityのList. <code>null</code>の場合は格納されない
1212
         * @return SQL実行結果
1213
         */
1214
        @SuppressWarnings("deprecation")
1215
        private <E> int batchUpdate(final Class<E> entityType, final Stream<E> entities,
1216
                        final UpdatesCondition<? super E> condition, final List<E> updatedEntities) {
1217
                var handler = this.getEntityHandler();
1✔
1218
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
1219
                        throw new IllegalArgumentException("Entity type not supported");
×
1220
                }
1221

1222
                try {
1223
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
1224
                        var context = handler.createBatchUpdateContext(this, metadata, entityType);
1✔
1225
                        context.setSqlKind(SqlKind.BATCH_UPDATE);
1✔
1226

1227
                        var versionColumn = MappingUtils.getVersionMappingColumn(metadata.getSchema(),
1✔
1228
                                        entityType);
1229
                        var entityCount = 0;
1✔
1230
                        var updateCount = 0;
1✔
1231
                        var entityList = new ArrayList<E>();
1✔
1232
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1233
                        var hasBeforeEntityBatchUpdateListener = eventListenerHolder.hasBeforeEntityBatchUpdateListener();
1✔
1234
                        var eventObj = hasBeforeEntityBatchUpdateListener
1✔
1235
                                        ? new BeforeEntityBatchUpdateEvent(context, null, entityType)
1✔
1236
                                        : null;
1✔
1237
                        for (var iterator = entities.iterator(); iterator.hasNext();) {
1✔
1238
                                var entity = iterator.next();
1✔
1239

1240
                                if (!entityType.isInstance(entity)) {
1✔
1241
                                        throw new IllegalArgumentException("Entity types do not match");
×
1242
                                }
1243

1244
                                entityList.add(entity);
1✔
1245
                                if (updatedEntities != null) {
1✔
1246
                                        updatedEntities.add(entity);
1✔
1247
                                }
1248
                                entityCount++;
1✔
1249

1250
                                // EntityBatchUpdate実行前イベント発行
1251
                                if (hasBeforeEntityBatchUpdateListener) {
1✔
1252
                                        // BatchUpdateでは大量のEntityが処理されるため、実行前イベントでは都度Eventインスタンスを生成せず、1つのイベントインスタンスを使いまわす実装とする(性能対応)
1253
                                        eventObj.setEntity(entity);
1✔
1254
                                        for (var listener : eventListenerHolder.getBeforeEntityBatchUpdateListeners()) {
1✔
1255
                                                listener.accept(eventObj);
1✔
1256
                                        }
1✔
1257
                                }
1258

1259
                                handler.setUpdateParams(context, entity);
1✔
1260
                                context.addBatch();
1✔
1261

1262
                                if (condition.test(context, context.batchCount(), entity)) {
1✔
1263
                                        updateCount += Arrays.stream(doBatchUpdate(handler, context, entityList, entityType)).sum();
1✔
1264
                                        entityList.clear();
1✔
1265
                                }
1266
                        }
1✔
1267
                        updateCount = updateCount + (context.batchCount() != 0
1✔
1268
                                        ? Arrays.stream(doBatchUpdate(handler, context, entityList, entityType)).sum()
1✔
1269
                                        : 0);
1✔
1270

1271
                        if (updatedEntities != null && versionColumn.isPresent()) {
1✔
1272
                                var vColumn = versionColumn.get();
1✔
1273
                                var keyColumns = metadata.getColumns().stream()
1✔
1274
                                                .filter(TableMetadata.Column::isKey)
1✔
1275
                                                .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
1276
                                                .map(c -> MappingUtils.getMappingColumnMap(metadata.getSchema(), entityType, SqlKind.NONE)
1✔
1277
                                                                .get(c.getCamelColumnName()))
1✔
1278
                                                .collect(Collectors.toList());
1✔
1279

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

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

1290
                                        for (var start = 0; start < entitySize; start = start + IN_CLAUSE_MAX_PARAM_SIZE) {
1✔
1291
                                                var end = Math.min(start + IN_CLAUSE_MAX_PARAM_SIZE, entitySize);
1✔
1292
                                                var subList = keyList.subList(start, end);
1✔
1293

1294
                                                query(entityType).in(keyColumn.getCamelName(), subList).stream()
1✔
1295
                                                                .map(e -> {
1✔
1296
                                                                        var updatedEntity = updatedEntityMap.get(keyColumn.getValue(e)).get(0);
1✔
1297
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
1✔
1298
                                                                        return updatedEntity;
1✔
1299
                                                                }).count();
1✔
1300
                                        }
1301
                                } else if (keyColumns.size() > 1) {
1✔
1302
                                        // 複合キーの場合はIN句で一括取得できないため1件ずつ取得して@Versionのついたフィールドを更新する
1303
                                        updatedEntities.stream()
1✔
1304
                                                        .map(updatedEntity -> {
1✔
1305
                                                                var keyValues = keyColumns.stream().map(k -> k.getValue(updatedEntity)).toArray();
×
1306
                                                                find(entityType, keyValues).ifPresent(e -> {
×
1307
                                                                        vColumn.setValue(updatedEntity, vColumn.getValue(e));
×
1308
                                                                });
×
1309
                                                                return updatedEntity;
×
1310
                                                        }).count();
1✔
1311
                                }
1312
                        }
1313
                        if (versionColumn.isPresent() && getDialect().supportsEntityBulkUpdateOptimisticLock()
1✔
1314
                                        && updateCount != entityCount) {
1315
                                // バージョンカラムの指定があり、更新件数と更新対象Entityの件数が不一致の場合は楽観ロックエラーとする
1316
                                throw new OptimisticLockException(String.format(
1✔
1317
                                                "An error occurred due to optimistic locking.%nExecuted SQL [%n%s]%nBatch Entity Count: %d, Update Count: %d.",
1318
                                                context.getExecutableSql(), entityCount, updateCount));
1✔
1319
                        }
1320
                        return updateCount;
1✔
1321
                } catch (SQLException ex) {
×
1322
                        throw new EntitySqlRuntimeException(SqlKind.BATCH_UPDATE, ex);
×
1323
                }
1324
        }
1325

1326
        private <E> int[] doBatchUpdate(final EntityHandler<Object> handler,
1327
                        final ExecutionContext context,
1328
                        final List<E> entityList,
1329
                        final Class<E> entityType) throws SQLException {
1330
                var counts = handler.doBatchUpdate(this, context);
1✔
1331

1332
                // EntityBatchUpdate実行後イベント発行
1333
                var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
1334
                if (eventListenerHolder.hasAfterEntityBatchUpdateListener()) {
1✔
1335
                        var eventObj = new AfterEntityBatchUpdateEvent(context, entityList, entityType, counts);
1✔
1336
                        for (var listener : eventListenerHolder.getAfterEntityBatchUpdateListeners()) {
1✔
1337
                                listener.accept(eventObj);
1✔
1338
                        }
1✔
1339
                        counts = eventObj.getCounts();
1✔
1340
                }
1341

1342
                return counts;
1✔
1343
        }
1344

1345
        /**
1346
         * {@inheritDoc}
1347
         *
1348
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext)
1349
         */
1350
        @Override
1351
        public ResultSet query(final ExecutionContext executionContext) throws SQLException {
1352
                // パラメータログを出力する
1353
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1354

1355
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1356
                        executionContext.setSqlKind(SqlKind.SELECT);
1✔
1357
                }
1358

1359
                // コンテキスト変換
1360
                transformContext(executionContext);
1✔
1361

1362
                var stmt = getPreparedStatement(executionContext);
1✔
1363

1364
                // INパラメータ設定
1365
                executionContext.bindParams(stmt);
1✔
1366

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

1370
                Instant startTime = null;
1✔
1371
                if (LOG.isDebugEnabled()) {
1✔
1372
                        LOG.debug("Execute query sql. sqlName: {}", executionContext.getSqlName());
×
1373
                }
1374
                if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1375
                        startTime = Instant.now(getSqlConfig().getClock());
1✔
1376
                }
1377

1378
                try {
1379
                        // デフォルト最大リトライ回数を取得し、個別指定(ExecutionContextの値)があれば上書き
1380
                        var maxRetryCount = getMaxRetryCount();
1✔
1381
                        if (executionContext.getMaxRetryCount() >= 0) {
1✔
1382
                                maxRetryCount = executionContext.getMaxRetryCount();
1✔
1383
                        }
1384

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

1468
        /**
1469
         * {@inheritDoc}
1470
         *
1471
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1472
         *      jp.co.future.uroborosql.converter.ResultSetConverter)
1473
         */
1474
        @Override
1475
        public <T> Stream<T> query(final ExecutionContext executionContext, final ResultSetConverter<T> converter)
1476
                        throws SQLException {
1477
                var rs = query(executionContext);
1✔
1478
                return StreamSupport.stream(new ResultSetSpliterator<>(rs, converter), false).onClose(() -> {
1✔
1479
                        try {
1480
                                if (rs != null && !rs.isClosed()) {
1✔
1481
                                        rs.close();
1✔
1482
                                }
1483
                        } catch (SQLException ex) {
×
1484
                                // do nothing
1485
                        }
1✔
1486
                });
1✔
1487
        }
1488

1489
        /**
1490
         * {@inheritDoc}
1491
         *
1492
         * @see jp.co.future.uroborosql.SqlAgent#query(jp.co.future.uroborosql.context.ExecutionContext,
1493
         *      jp.co.future.uroborosql.utils.CaseFormat)
1494
         */
1495
        @Override
1496
        public List<Map<String, Object>> query(final ExecutionContext executionContext, final CaseFormat caseFormat)
1497
                        throws SQLException {
1498
                try (var stream = query(executionContext, new MapResultSetConverter(getSqlConfig(), caseFormat))) {
1✔
1499
                        return stream.collect(Collectors.toList());
1✔
1500
                }
1501
        }
1502

1503
        /**
1504
         * @see jp.co.future.uroborosql.SqlAgent#update(jp.co.future.uroborosql.context.ExecutionContext)
1505
         */
1506
        @Override
1507
        public int update(final ExecutionContext executionContext) throws SQLException {
1508
                // パラメータログを出力する
1509
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1510

1511
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1512
                        executionContext.setSqlKind(SqlKind.UPDATE);
1✔
1513
                }
1514

1515
                // コンテキスト変換
1516
                transformContext(executionContext);
1✔
1517

1518
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1519
                if (executionContext.getUpdateDelegate() != null) {
1✔
1520
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1521
                        if (LOG.isInfoEnabled()) {
1✔
1522
                                LOG.info("Performs update delegate of update process.");
1✔
1523
                        }
1524
                        return executionContext.getUpdateDelegate().apply(executionContext);
1✔
1525
                }
1526

1527
                Instant startTime = null;
1✔
1528

1529
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1530

1531
                        // INパラメータ設定
1532
                        executionContext.bindParams(stmt);
1✔
1533

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

1537
                        if (LOG.isDebugEnabled()) {
1✔
1538
                                LOG.debug("Execute update sql. sqlName: {}", executionContext.getSqlName());
×
1539
                        }
1540

1541
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1542
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1543
                        }
1544

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

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

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

1644
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1645
                        executionContext.setSqlKind(SqlKind.BATCH_INSERT);
1✔
1646
                }
1647

1648
                // コンテキスト変換
1649
                transformContext(executionContext);
1✔
1650

1651
                // 更新移譲処理の指定がある場合は移譲処理を実行し結果を返却
1652
                if (executionContext.getUpdateDelegate() != null) {
1✔
1653
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1654
                        if (LOG.isInfoEnabled()) {
1✔
1655
                                LOG.info("Performs update delegate of batch process.");
1✔
1656
                        }
1657
                        return new int[] { executionContext.getUpdateDelegate().apply(executionContext) };
1✔
1658
                }
1659

1660
                Instant startTime = null;
1✔
1661

1662
                try (var stmt = getPreparedStatement(executionContext)) {
1✔
1663

1664
                        // INパラメータ設定
1665
                        executionContext.bindBatchParams(stmt);
1✔
1666

1667
                        if (LOG.isDebugEnabled()) {
1✔
1668
                                LOG.debug("Execute batch sql. sqlName: {}", executionContext.getSqlName());
×
1669
                        }
1670
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1671
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1672
                        }
1673

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

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

1765
        /**
1766
         * {@inheritDoc}
1767
         *
1768
         * @see jp.co.future.uroborosql.SqlAgent#procedure(jp.co.future.uroborosql.context.ExecutionContext)
1769
         */
1770
        @Override
1771
        public Map<String, Object> procedure(final ExecutionContext executionContext) throws SQLException {
1772
                // パラメータログを出力する
1773
                MDC.put(SUPPRESS_PARAMETER_LOG_OUTPUT, Boolean.FALSE.toString());
1✔
1774

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

1778
                if (SqlKind.NONE.equals(executionContext.getSqlKind())) {
1✔
1779
                        executionContext.setSqlKind(SqlKind.PROCEDURE);
1✔
1780
                }
1781

1782
                // コンテキスト変換
1783
                transformContext(executionContext);
1✔
1784

1785
                Instant startTime = null;
1✔
1786

1787
                try (var callableStatement = getCallableStatement(executionContext)) {
1✔
1788

1789
                        // パラメータ設定
1790
                        executionContext.bindParams(callableStatement);
1✔
1791

1792
                        if (LOG.isDebugEnabled()) {
1✔
1793
                                LOG.debug("Execute stored procedure. sqlName: {}", executionContext.getSqlName());
×
1794
                        }
1795
                        if (PERFORMANCE_LOG.isInfoEnabled()) {
1✔
1796
                                startTime = Instant.now(getSqlConfig().getClock());
1✔
1797
                        }
1798

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

1805
                        // デフォルトリトライ待機時間を取得し、個別指定(ExecutionContextの値)があれば上書き
1806
                        var retryWaitTime = getRetryWaitTime();
1✔
1807
                        if (executionContext.getRetryWaitTime() > 0) {
1✔
1808
                                retryWaitTime = executionContext.getRetryWaitTime();
1✔
1809
                        }
1810
                        var loopCount = 0;
1✔
1811
                        do {
1812
                                try {
1813
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1814
                                                setSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1815
                                        }
1816
                                        var result = callableStatement.execute();
1✔
1817
                                        // Procedure実行後イベント発行
1818
                                        if (getSqlConfig().getEventListenerHolder().hasAfterProcedureListener()) {
1✔
1819
                                                var eventObj = new AfterProcedureEvent(executionContext, result, callableStatement.getConnection(),
1✔
1820
                                                                callableStatement);
1821
                                                for (var listener : getSqlConfig().getEventListenerHolder().getAfterProcedureListeners()) {
1✔
1822
                                                        listener.accept(eventObj);
1✔
1823
                                                }
1✔
1824
                                                result = eventObj.isResult();
1✔
1825
                                        }
1826
                                        break;
1827
                                } catch (SQLException ex) {
1✔
1828
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1829
                                                rollback(RETRY_SAVEPOINT_NAME);
1✔
1830
                                        }
1831
                                        if (maxRetryCount > loopCount) {
1✔
1832
                                                var errorCode = String.valueOf(ex.getErrorCode());
1✔
1833
                                                var sqlState = ex.getSQLState();
1✔
1834
                                                if (getSqlRetryCodes().contains(errorCode) || getSqlRetryCodes().contains(sqlState)) {
1✔
1835
                                                        if (LOG.isDebugEnabled()) {
1✔
1836
                                                                LOG.debug(String.format(
×
1837
                                                                                "Caught the error code to be retried.(%d times). Retry after %,3d ms.",
1838
                                                                                loopCount + 1, retryWaitTime));
×
1839
                                                        }
1840
                                                        if (retryWaitTime > 0) {
1✔
1841
                                                                try {
1842
                                                                        Thread.sleep(retryWaitTime);
1✔
1843
                                                                } catch (InterruptedException ie) {
×
1844
                                                                        // do nothing
1845
                                                                }
1✔
1846
                                                        }
1847
                                                } else {
1848
                                                        throw ex;
1✔
1849
                                                }
1850
                                        } else {
1✔
1851
                                                throw ex;
1✔
1852
                                        }
1853
                                } finally {
1854
                                        if (maxRetryCount > 0 && getDialect().isRollbackToSavepointBeforeRetry()) {
1✔
1855
                                                releaseSavepoint(RETRY_SAVEPOINT_NAME);
1✔
1856
                                        }
1857
                                        executionContext.contextAttrs().put(CTX_ATTR_KEY_RETRY_COUNT, loopCount);
1✔
1858
                                }
1859
                        } while (maxRetryCount > loopCount++);
1✔
1860
                        // 結果取得
1861
                        return executionContext.getOutParams(callableStatement);
1✔
1862
                } catch (SQLException ex) {
1✔
1863
                        handleException(executionContext, ex);
×
1864
                } finally {
1865
                        if (PERFORMANCE_LOG.isInfoEnabled() && startTime != null) {
1✔
1866
                                PERFORMANCE_LOG.info("Stored procedure execution time [{}({})] : [{}]",
1✔
1867
                                                generateSqlName(executionContext),
1✔
1868
                                                executionContext.getSqlKind(),
1✔
1869
                                                formatElapsedTime(startTime, Instant.now(getSqlConfig().getClock())));
1✔
1870
                        }
1871
                        MDC.remove(SUPPRESS_PARAMETER_LOG_OUTPUT);
1✔
1872
                }
1873
                return null;
×
1874
        }
1875

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

1891
                // SQL-IDの付与
1892
                if (originalSql.contains(keySqlId)) {
1✔
1893
                        var sqlId = executionContext.getSqlId();
1✔
1894
                        if (ObjectUtils.isEmpty(sqlId)) {
1✔
1895
                                sqlId = sqlName;
1✔
1896
                        }
1897
                        if (ObjectUtils.isEmpty(sqlId)) {
1✔
1898
                                sqlId = String.valueOf(originalSql.hashCode());
×
1899
                        }
1900

1901
                        originalSql = originalSql.replace(keySqlId, sqlId);
1✔
1902
                }
1903

1904
                if (SQL_LOG.isInfoEnabled() && sqlName != null) {
1✔
1905
                        if (executionContext.getSqlKind().isEntityType()) {
1✔
1906
                                SQL_LOG.info("EntityClass : {}", sqlName);
1✔
1907
                        } else if (getSqlResourceManager().existSql(sqlName)) {
1✔
1908
                                SQL_LOG.info("SQLPath : {}", getSqlResourceManager().getSqlPath(sqlName));
1✔
1909
                        }
1910
                }
1911

1912
                // SQL変換前イベント発行
1913
                if (getSqlConfig().getEventListenerHolder().hasBeforeTransformSqlListener()) {
1✔
1914
                        var eventObj = new BeforeTransformSqlEvent(executionContext, originalSql);
1✔
1915
                        getSqlConfig().getEventListenerHolder().getBeforeTransformSqlListeners()
1✔
1916
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1917
                        originalSql = eventObj.getSql();
1✔
1918
                }
1919
                executionContext.setSql(originalSql);
1✔
1920

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

1924
                // SQLパース前イベントの呼出
1925
                if (executionContext.batchCount() == 0
1✔
1926
                                && getSqlConfig().getEventListenerHolder().hasBeforeParseSqlListener()) {
1✔
1927
                        var eventObj = new BeforeParseSqlEvent(executionContext);
1✔
1928
                        getSqlConfig().getEventListenerHolder().getBeforeParseSqlListeners()
1✔
1929
                                        .forEach(listener -> listener.accept(eventObj));
1✔
1930
                }
1931

1932
                if (SQL_LOG.isDebugEnabled()) {
1✔
1933
                        SQL_LOG.debug("Template SQL[{}{}{}]", System.lineSeparator(), originalSql, System.lineSeparator());
×
1934
                }
1935

1936
                if (ObjectUtils.isEmpty(executionContext.getExecutableSql())) {
1✔
1937
                        // SQLパーサーによるパース処理
1938
                        var outputBindComment = (boolean) executionContext.contextAttrs().getOrDefault(
1✔
1939
                                        CTX_ATTR_KEY_OUTPUT_BIND_COMMENT, true);
1✔
1940
                        var sqlParser = new SqlParserImpl(originalSql, sqlConfig.getExpressionParser(),
1✔
1941
                                        getDialect().isRemoveTerminator(), outputBindComment);
1✔
1942
                        var contextTransformer = sqlParser.parse();
1✔
1943
                        contextTransformer.transform(executionContext);
1✔
1944

1945
                        if (COVERAGE_HANDLER_REF.get() != null) {
1✔
1946
                                // SQLカバレッジ用のログを出力する
1947
                                var coverageData = new CoverageData(sqlName, originalSql,
1✔
1948
                                                contextTransformer.getPassedRoute());
1✔
1949
                                if (COVERAGE_LOG.isDebugEnabled()) {
1✔
1950
                                        COVERAGE_LOG.debug("coverage data: {}", coverageData);
×
1951
                                }
1952

1953
                                COVERAGE_HANDLER_REF.get().accept(coverageData);
1✔
1954
                        }
1955
                }
1956

1957
                if (SQL_LOG.isInfoEnabled()) {
1✔
1958
                        SQL_LOG.info("Executed SQL[{}{}{}]", System.lineSeparator(), executionContext.getExecutableSql(),
1✔
1959
                                        System.lineSeparator());
1✔
1960
                }
1961
        }
1✔
1962

1963
        /** 時間計測用のログに出力するSQL名を生成する.
1964
         *
1965
         * @param executionContext ExecutionContext
1966
         * @return SQL名. SQL名が取得できない場合はSQL_ID、または空文字を返却する
1967
         */
1968
        private String generateSqlName(final ExecutionContext executionContext) {
1969
                if (executionContext.getSqlName() != null) {
1✔
1970
                        return executionContext.getSqlName();
1✔
1971
                } else {
1972
                        return Objects.toString(executionContext.getSqlId(), "");
1✔
1973
                }
1974
        }
1975

1976
        /**
1977
         * 例外発生時ハンドラー
1978
         *
1979
         * @param executionContext ExecutionContext
1980
         * @param ex SQL例外
1981
         * @throws SQLException SQL例外
1982
         */
1983
        private void handleException(final ExecutionContext executionContext, final SQLException ex) throws SQLException {
1984
                var cause = ex;
1✔
1985

1986
                while (cause.getNextException() != null) {
1✔
1987
                        cause = cause.getNextException();
1✔
1988
                }
1989

1990
                if (outputExceptionLog && LOG.isErrorEnabled()) {
1✔
1991
                        var builder = new StringBuilder();
1✔
1992
                        builder.append(System.lineSeparator()).append("Exception occurred in SQL execution.")
1✔
1993
                                        .append(System.lineSeparator());
1✔
1994
                        builder.append("Executed SQL[").append(executionContext.getExecutableSql()).append("]")
1✔
1995
                                        .append(System.lineSeparator());
1✔
1996
                        if (executionContext instanceof ExecutionContextImpl) {
1✔
1997
                                var bindParameters = ((ExecutionContextImpl) executionContext).getBindParameters();
1✔
1998
                                for (var i = 0; i < bindParameters.length; i++) {
1✔
1999
                                        var parameter = bindParameters[i];
1✔
2000
                                        builder.append("Bind Parameter.[INDEX[").append(i + 1).append("], ").append(parameter.toString())
1✔
2001
                                                        .append("]").append(System.lineSeparator());
1✔
2002
                                }
2003
                        }
2004
                        LOG.error(builder.toString(), cause);
1✔
2005
                }
2006

2007
                throw cause;
1✔
2008
        }
2009

2010
        /**
2011
         * REPLでSQLを実行するためのコマンドをログとしてREPL_LOGに出力する.
2012
         *
2013
         * @param executionContext executionContext
2014
         */
2015
        private void outputReplLog(final ExecutionContext executionContext) {
2016
                if (!(REPL_LOG.isInfoEnabled() &&
1✔
2017
                                executionContext.getSqlName() != null &&
1✔
2018
                                (SqlKind.SELECT.equals(executionContext.getSqlKind()) ||
1✔
2019
                                                SqlKind.UPDATE.equals(executionContext.getSqlKind())))) {
1✔
2020
                        // REPLログ出力対象でない場合は何もしない
2021
                        return;
1✔
2022
                }
2023

2024
                var builder = new StringBuilder();
1✔
2025

2026
                if (SqlKind.SELECT.equals(executionContext.getSqlKind())) {
1✔
2027
                        builder.append("query ");
1✔
2028
                } else {
2029
                        builder.append("update ");
1✔
2030
                }
2031

2032
                builder.append(executionContext.getSqlName());
1✔
2033

2034
                var params = new ArrayList<Parameter>();
1✔
2035
                for (var bindName : executionContext.getBindNames()) {
1✔
2036
                        params.add(executionContext.getParam(bindName));
1✔
2037
                }
1✔
2038
                if (!params.isEmpty()) {
1✔
2039
                        builder.append(" ");
1✔
2040
                        builder.append(SqlParamUtils.formatPrams(params));
1✔
2041
                }
2042
                REPL_LOG.info("REPL command: {}", builder.toString());
1✔
2043
        }
1✔
2044

2045
        /**
2046
         *
2047
         * {@inheritDoc}
2048
         *
2049
         * @see jp.co.future.uroborosql.SqlAgent#find(java.lang.Class, java.lang.Object[])
2050
         */
2051
        @SuppressWarnings("unchecked")
2052
        @Override
2053
        public <E> Optional<E> find(final Class<? extends E> entityType, final Object... keys) {
2054
                @SuppressWarnings("rawtypes")
2055
                EntityHandler handler = this.getEntityHandler();
1✔
2056
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2057
                        throw new IllegalArgumentException("Entity type not supported");
×
2058
                }
2059

2060
                try {
2061
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2062
                        var keyNames = metadata.getColumns().stream()
1✔
2063
                                        .filter(TableMetadata.Column::isKey)
1✔
2064
                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
2065
                                        .map(TableMetadata.Column::getColumnName)
1✔
2066
                                        .map(CaseFormat.CAMEL_CASE::convert)
1✔
2067
                                        .toArray(String[]::new);
1✔
2068

2069
                        if (keyNames.length != keys.length) {
1✔
2070
                                throw new IllegalArgumentException("Number of keys does not match");
×
2071
                        }
2072
                        var params = new HashMap<String, Object>();
1✔
2073
                        for (var i = 0; i < keys.length; i++) {
1✔
2074
                                params.put(keyNames[i], keys[i]);
1✔
2075
                        }
2076

2077
                        var context = handler.createSelectContext(this, metadata, entityType, true);
1✔
2078
                        context.setSqlKind(SqlKind.ENTITY_SELECT).paramMap(params);
1✔
2079

2080
                        // EntityQuery実行前イベント発行
2081
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2082
                        if (eventListenerHolder.hasBeforeEntityQueryListener()) {
1✔
NEW
2083
                                var eventObj = new BeforeEntityQueryEvent(context, null, entityType);
×
NEW
2084
                                for (var listener : eventListenerHolder.getBeforeEntityQueryListeners()) {
×
NEW
2085
                                        listener.accept(eventObj);
×
NEW
2086
                                }
×
2087
                        }
2088

2089
                        var results = handler.doSelect(this, context, entityType);
1✔
2090

2091
                        // EntityQuery実行後イベント発行
2092
                        if (eventListenerHolder.hasAfterEntityQueryListener()) {
1✔
NEW
2093
                                var eventObj = new AfterEntityQueryEvent(context(), null, entityType, results);
×
NEW
2094
                                for (var listener : eventListenerHolder.getAfterEntityQueryListeners()) {
×
NEW
2095
                                        listener.accept(eventObj);
×
NEW
2096
                                }
×
NEW
2097
                                results = eventObj.getResults();
×
2098
                        }
2099

2100
                        try (var stream = results) {
1✔
2101
                                return stream.findFirst();
1✔
2102
                        }
2103
                } catch (SQLException ex) {
×
2104
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_SELECT, ex);
×
2105
                }
2106
        }
2107

2108
        /**
2109
         * {@inheritDoc}
2110
         *
2111
         * @see jp.co.future.uroborosql.SqlAgent#insert(java.lang.Object)
2112
         */
2113
        @SuppressWarnings("unchecked")
2114
        @Override
2115
        public <E> int insert(final E entity) {
2116
                if (entity instanceof Stream) {
1✔
2117
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2118
                }
2119

2120
                @SuppressWarnings("rawtypes")
2121
                EntityHandler handler = this.getEntityHandler();
1✔
2122
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2123
                        throw new IllegalArgumentException("Entity type not supported");
×
2124
                }
2125

2126
                try {
2127
                        var entityType = entity.getClass();
1✔
2128
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2129
                        var context = handler.createInsertContext(this, metadata, entityType);
1✔
2130
                        context.setSqlKind(SqlKind.ENTITY_INSERT);
1✔
2131

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

2136
                        // EntityInsert実行前イベント発行
2137
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2138
                        if (eventListenerHolder.hasBeforeEntityInsertListener()) {
1✔
2139
                                var eventObj = new BeforeEntityInsertEvent(context, entity, entityType);
1✔
2140
                                for (var listener : eventListenerHolder.getBeforeEntityInsertListeners()) {
1✔
2141
                                        listener.accept(eventObj);
1✔
2142
                                }
1✔
2143
                        }
2144
                        handler.setInsertParams(context, entity);
1✔
2145

2146
                        var count = handler.doInsert(this, context, entity);
1✔
2147

2148
                        // EntityInsert実行後イベント発行
2149
                        if (eventListenerHolder.hasAfterEntityInsertListener()) {
1✔
2150
                                var eventObj = new AfterEntityInsertEvent(context, entity, entityType, count);
1✔
2151
                                for (var listener : eventListenerHolder.getAfterEntityInsertListeners()) {
1✔
2152
                                        listener.accept(eventObj);
1✔
2153
                                }
1✔
2154
                                count = eventObj.getCount();
1✔
2155
                        }
2156

2157
                        if (!autoGeneratedColumns.isEmpty()) {
1✔
2158
                                var ids = context.getGeneratedKeyValues();
1✔
2159
                                var idx = 0;
1✔
2160
                                for (var col : autoGeneratedColumns) {
1✔
2161
                                        setEntityIdValue(entity, ids[idx++], col);
1✔
2162
                                }
1✔
2163
                        }
2164
                        return count;
1✔
2165
                } catch (SQLException ex) {
1✔
2166
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_INSERT, ex);
1✔
2167
                }
2168
        }
2169

2170
        /**
2171
         * {@inheritDoc}
2172
         *
2173
         * @see jp.co.future.uroborosql.SqlAgent#insertAndReturn(java.lang.Object)
2174
         */
2175
        @Override
2176
        public <E> E insertAndReturn(final E entity) {
2177
                insert(entity);
1✔
2178
                return entity;
1✔
2179
        }
2180

2181
        /**
2182
         * {@inheritDoc}
2183
         *
2184
         * @see jp.co.future.uroborosql.SqlAgent#update(java.lang.Object)
2185
         */
2186
        @SuppressWarnings("unchecked")
2187
        @Override
2188
        public <E> int update(final E entity) {
2189
                if (entity instanceof Stream) {
1✔
2190
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2191
                }
2192

2193
                @SuppressWarnings("rawtypes")
2194
                EntityHandler handler = this.getEntityHandler();
1✔
2195
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2196
                        throw new IllegalArgumentException("Entity type not supported");
×
2197
                }
2198

2199
                try {
2200
                        var entityType = entity.getClass();
1✔
2201
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2202
                        var context = handler.createUpdateContext(this, metadata, entityType, true);
1✔
2203
                        context.setSqlKind(SqlKind.ENTITY_UPDATE);
1✔
2204

2205
                        // EntityUpdate実行前イベント発行
2206
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2207
                        if (eventListenerHolder.hasBeforeEntityUpdateListener()) {
1✔
2208
                                var eventObj = new BeforeEntityUpdateEvent(context, entity, entityType);
1✔
2209
                                for (var listener : eventListenerHolder.getBeforeEntityUpdateListeners()) {
1✔
2210
                                        listener.accept(eventObj);
1✔
2211
                                }
1✔
2212
                        }
2213
                        handler.setUpdateParams(context, entity);
1✔
2214

2215
                        var count = handler.doUpdate(this, context, entity);
1✔
2216

2217
                        // EntityUpdate実行後イベント発行
2218
                        if (eventListenerHolder.hasAfterEntityUpdateListener()) {
1✔
2219
                                var eventObj = new AfterEntityUpdateEvent(context, entity, entityType, count);
1✔
2220
                                for (var listener : eventListenerHolder.getAfterEntityUpdateListeners()) {
1✔
2221
                                        listener.accept(eventObj);
1✔
2222
                                }
1✔
2223
                                count = eventObj.getCount();
1✔
2224
                        }
2225

2226
                        var updateCount = count;
1✔
2227
                        MappingUtils.getVersionMappingColumn(metadata.getSchema(), entityType).ifPresent(versionColumn -> {
1✔
2228
                                if (updateCount == 0) {
1✔
2229
                                        throw new OptimisticLockException(context);
1✔
2230
                                } else {
2231
                                        var columnMap = MappingUtils.getMappingColumnMap(metadata.getSchema(), entityType, SqlKind.NONE);
1✔
2232
                                        var keys = metadata.getColumns().stream()
1✔
2233
                                                        .filter(TableMetadata.Column::isKey)
1✔
2234
                                                        .sorted(Comparator.comparingInt(TableMetadata.Column::getKeySeq))
1✔
2235
                                                        .map(c -> columnMap.get(c.getCamelColumnName()).getValue(entity))
1✔
2236
                                                        .toArray();
1✔
2237

2238
                                        find(entityType, keys).ifPresent(e -> versionColumn.setValue(entity, versionColumn.getValue(e)));
1✔
2239
                                }
2240
                        });
1✔
2241
                        return updateCount;
1✔
2242
                } catch (SQLException ex) {
×
2243
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_UPDATE, ex);
×
2244
                }
2245
        }
2246

2247
        /**
2248
         * {@inheritDoc}
2249
         *
2250
         * @see jp.co.future.uroborosql.SqlAgent#updateAndReturn(java.lang.Object)
2251
         */
2252
        @Override
2253
        public <E> E updateAndReturn(final E entity) {
2254
                update(entity);
1✔
2255
                return entity;
1✔
2256
        }
2257

2258
        /**
2259
         * {@inheritDoc}
2260
         *
2261
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class)
2262
         */
2263
        @Override
2264
        public <E> SqlEntityUpdate<E> update(final Class<? extends E> entityType) {
2265
                var handler = this.getEntityHandler();
1✔
2266
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2267
                        throw new IllegalArgumentException("Entity type not supported");
×
2268
                }
2269

2270
                try {
2271
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2272

2273
                        var context = handler.createUpdateContext(this, metadata, entityType, false);
1✔
2274
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2275

2276
                        return new SqlEntityUpdateImpl<>(this, handler, metadata, context, entityType);
1✔
2277
                } catch (SQLException ex) {
×
2278
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2279
                }
2280
        }
2281

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

2293
        /**
2294
         * {@inheritDoc}
2295
         *
2296
         * @see jp.co.future.uroborosql.SqlAgent#mergeAndReturn(java.lang.Object)
2297
         */
2298
        @Override
2299
        public <E> E mergeAndReturn(final E entity) {
2300
                return doMergeAndReturn(entity, false);
1✔
2301
        }
2302

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

2314
        /**
2315
         * {@inheritDoc}
2316
         *
2317
         * @see jp.co.future.uroborosql.SqlAgent#mergeWithLockingAndReturn(java.lang.Object)
2318
         */
2319
        @Override
2320
        public <E> E mergeWithLockingAndReturn(final E entity) {
2321
                return doMergeAndReturn(entity, true);
1✔
2322
        }
2323

2324
        /**
2325
         * エンティティのMERGE(INSERTまたはUPDATE)を実行し、MERGEしたエンティティを返却する.<br>
2326
         * locking引数が<code>true</code>の場合、マージの最初に発行するEntityの検索で悲観ロック(forUpdateNoWait)を行う
2327
         *
2328
         * @param <E> エンティティ型
2329
         * @param entity エンティティ
2330
         * @param locking エンティティの悲観ロックを行うかどうか
2331
         * @return MERGEしたエンティティ
2332
         *
2333
         */
2334
        @SuppressWarnings("unchecked")
2335
        private <E> E doMergeAndReturn(final E entity, final boolean locking) {
2336
                if (entity instanceof Stream) {
1✔
2337
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2338
                }
2339

2340
                var handler = this.getEntityHandler();
1✔
2341
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2342
                        throw new IllegalArgumentException("Entity type not supported.");
×
2343
                }
2344

2345
                var type = entity.getClass();
1✔
2346
                try {
2347
                        var metadata = handler.getMetadata(this.transactionManager, type);
1✔
2348
                        var keyColumns = metadata.getKeyColumns();
1✔
2349

2350
                        if (keyColumns.isEmpty()) {
1✔
2351
                                throw new IllegalArgumentException("Entity has no keys.");
×
2352
                        }
2353

2354
                        var mappingColumns = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.UPDATE);
1✔
2355
                        var query = (SqlEntityQuery<E>) query(type);
1✔
2356
                        for (var column : keyColumns) {
1✔
2357
                                var camelName = column.getCamelColumnName();
1✔
2358
                                query.equal(camelName, mappingColumns.get(camelName).getValue(entity));
1✔
2359
                        }
1✔
2360
                        if (locking) {
1✔
2361
                                query.forUpdateNoWait();
1✔
2362
                        }
2363
                        return query.first()
1✔
2364
                                        .map(findEntity -> {
1✔
2365
                                                for (var mappingColumn : mappingColumns.values()) {
1✔
2366
                                                        if (!mappingColumn.isId()) {
1✔
2367
                                                                var value = mappingColumn.getValue(entity);
1✔
2368
                                                                if (value != null) {
1✔
2369
                                                                        mappingColumn.setValue(findEntity, value);
1✔
2370
                                                                }
2371
                                                        }
2372
                                                }
1✔
2373
                                                return updateAndReturn(findEntity);
1✔
2374
                                        }).orElseGet(() -> insertAndReturn(entity));
1✔
2375
                } catch (SQLException ex) {
×
2376
                        throw new EntitySqlRuntimeException(SqlKind.MERGE, ex);
×
2377
                }
2378
        }
2379

2380
        /**
2381
         * {@inheritDoc}
2382
         *
2383
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Object)
2384
         */
2385
        @Override
2386
        public <E> int delete(final E entity) {
2387
                if (entity instanceof Stream) {
1✔
2388
                        throw new IllegalArgumentException("Stream type not supported.");
1✔
2389
                }
2390

2391
                var handler = this.getEntityHandler();
1✔
2392
                if (!handler.getEntityType().isInstance(entity)) {
1✔
2393
                        throw new IllegalArgumentException("Entity type not supported");
×
2394
                }
2395

2396
                try {
2397
                        var entityType = entity.getClass();
1✔
2398
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2399
                        var context = handler.createDeleteContext(this, metadata, entityType, true);
1✔
2400
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2401

2402
                        // EntityDelete実行前イベント発行
2403
                        var eventListenerHolder = getSqlConfig().getEventListenerHolder();
1✔
2404
                        if (eventListenerHolder.hasBeforeEntityDeleteListener()) {
1✔
2405
                                var eventObj = new BeforeEntityDeleteEvent(context, entity, entityType);
1✔
2406
                                for (var listener : eventListenerHolder.getBeforeEntityDeleteListeners()) {
1✔
2407
                                        listener.accept(eventObj);
1✔
2408
                                }
1✔
2409
                        }
2410
                        handler.setDeleteParams(context, entity);
1✔
2411

2412
                        var count = handler.doDelete(this, context, entity);
1✔
2413

2414
                        // EntityDelete実行後イベント発行
2415
                        if (eventListenerHolder.hasAfterEntityDeleteListener()) {
1✔
2416
                                var eventObj = new AfterEntityDeleteEvent(context, entity, entityType, count);
1✔
2417
                                for (var listener : eventListenerHolder.getAfterEntityDeleteListeners()) {
1✔
2418
                                        listener.accept(eventObj);
1✔
2419
                                }
1✔
2420
                                count = eventObj.getCount();
1✔
2421
                        }
2422

2423
                        return count;
1✔
2424
                } catch (SQLException ex) {
×
2425
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2426
                }
2427
        }
2428

2429
        /**
2430
         * {@inheritDoc}
2431
         *
2432
         * @see jp.co.future.uroborosql.SqlAgent#deleteAndReturn(java.lang.Object)
2433
         */
2434
        @Override
2435
        public <E> E deleteAndReturn(final E entity) {
2436
                delete(entity);
1✔
2437
                return entity;
1✔
2438
        }
2439

2440
        /**
2441
         * {@inheritDoc}
2442
         *
2443
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class, java.lang.Object[])
2444
         */
2445
        @Override
2446
        public <E> int delete(final Class<? extends E> entityType, final Object... keys) {
2447
                var handler = this.getEntityHandler();
1✔
2448
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2449
                        throw new IllegalArgumentException("Entity type not supported");
×
2450
                }
2451

2452
                try {
2453
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2454
                        TableMetadata.Column keyColumn = null;
1✔
2455
                        var keyColumns = metadata.getKeyColumns();
1✔
2456
                        if (keyColumns.size() == 1) {
1✔
2457
                                keyColumn = keyColumns.get(0);
1✔
2458
                        } else if (keyColumns.isEmpty()) {
1✔
2459
                                keyColumn = metadata.getColumns().get(0);
1✔
2460
                        } else {
2461
                                throw new IllegalArgumentException("Entity has multiple keys");
1✔
2462
                        }
2463
                        return delete(entityType).in(keyColumn.getCamelColumnName(), keys).count();
1✔
2464
                } catch (SQLException ex) {
×
2465
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2466
                }
2467
        }
2468

2469
        /**
2470
         * {@inheritDoc}
2471
         *
2472
         * @see jp.co.future.uroborosql.SqlAgent#delete(java.lang.Class)
2473
         */
2474
        @Override
2475
        public <E> SqlEntityDelete<E> delete(final Class<? extends E> entityType) {
2476
                var handler = this.getEntityHandler();
1✔
2477
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2478
                        throw new IllegalArgumentException("Entity type not supported");
×
2479
                }
2480

2481
                try {
2482
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2483
                        var context = handler.createDeleteContext(this, metadata, entityType, false);
1✔
2484
                        context.setSqlKind(SqlKind.ENTITY_DELETE);
1✔
2485
                        return new SqlEntityDeleteImpl<>(this, handler, metadata, context, entityType);
1✔
2486
                } catch (SQLException ex) {
×
2487
                        throw new EntitySqlRuntimeException(SqlKind.ENTITY_DELETE, ex);
×
2488
                }
2489
        }
2490

2491
        /**
2492
         * {@inheritDoc}
2493
         *
2494
         * @see jp.co.future.uroborosql.SqlAgent#truncate(java.lang.Class)
2495
         */
2496
        @Override
2497
        public <E> SqlAgent truncate(final Class<? extends E> entityType) {
2498
                var handler = this.getEntityHandler();
1✔
2499
                if (!handler.getEntityType().isAssignableFrom(entityType)) {
1✔
2500
                        throw new IllegalArgumentException("Entity type not supported");
×
2501
                }
2502
                try {
2503
                        var metadata = handler.getMetadata(this.transactionManager, entityType);
1✔
2504
                        var context = this.context().setSql("truncate table " + metadata.getTableIdentifier());
1✔
2505
                        context.setSqlKind(SqlKind.TRUNCATE);
1✔
2506
                        update(context);
1✔
2507
                        return this;
1✔
2508
                } catch (SQLException ex) {
1✔
2509
                        throw new EntitySqlRuntimeException(SqlKind.TRUNCATE, ex);
1✔
2510
                }
2511
        }
2512

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

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

2569
                if (!autoGeneratedColumnNames.isEmpty()) {
1✔
2570
                        context.setGeneratedKeyColumns(
1✔
2571
                                        autoGeneratedColumnNames.toArray(new String[autoGeneratedColumnNames.size()]));
1✔
2572
                }
2573
                return autoGeneratedColumns;
1✔
2574
        }
2575

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

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

2654
        /**
2655
         *
2656
         * {@inheritDoc}
2657
         *
2658
         * @see jp.co.future.uroborosql.SqlAgent#context()
2659
         */
2660
        @Override
2661
        public ExecutionContext context() {
2662
                return sqlConfig.context();
1✔
2663
        }
2664

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

2675
        /**
2676
         * {@inheritDoc}
2677
         *
2678
         * @see jp.co.future.uroborosql.SqlAgent#setFetchSize(int)
2679
         */
2680
        @Override
2681
        public SqlAgent setFetchSize(final int fetchSize) {
2682
                this.fetchSize = fetchSize;
1✔
2683
                return this;
1✔
2684
        }
2685

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

2696
        /**
2697
         * {@inheritDoc}
2698
         *
2699
         * @see jp.co.future.uroborosql.SqlAgent#setQueryTimeout(int)
2700
         */
2701
        @Override
2702
        public SqlAgent setQueryTimeout(final int queryTimeout) {
2703
                this.queryTimeout = queryTimeout;
1✔
2704
                return this;
1✔
2705
        }
2706

2707
        /**
2708
         * SQL実行をリトライするSQLエラーコードのリスト を取得します
2709
         *
2710
         * @return SQL実行をリトライするSQLエラーコードのリスト
2711
         */
2712
        public List<String> getSqlRetryCodes() {
2713
                return sqlRetryCodes;
1✔
2714
        }
2715

2716
        /**
2717
         * SQL実行をリトライするSQLエラーコードのリスト を設定します
2718
         *
2719
         * @param sqlRetryCodes SQL実行をリトライするSQLエラーコードのリスト
2720
         * @return SqlAgent
2721
         */
2722
        public SqlAgent setSqlRetryCodes(final List<String> sqlRetryCodes) {
2723
                this.sqlRetryCodes = sqlRetryCodes;
1✔
2724
                return this;
1✔
2725
        }
2726

2727
        /**
2728
         * 最大リトライ回数 を取得します
2729
         *
2730
         * @return 最大リトライ回数
2731
         */
2732
        public int getMaxRetryCount() {
2733
                return maxRetryCount;
1✔
2734
        }
2735

2736
        /**
2737
         * 最大リトライ回数 を設定します
2738
         *
2739
         * @param maxRetryCount 最大リトライ回数
2740
         * @return SqlAgent
2741
         */
2742
        public SqlAgent setMaxRetryCount(final int maxRetryCount) {
2743
                this.maxRetryCount = maxRetryCount;
1✔
2744
                return this;
1✔
2745
        }
2746

2747
        /**
2748
         * リトライタイムアウト時間(ms) を取得します
2749
         *
2750
         * @return リトライタイムアウト時間(ms)
2751
         */
2752
        public int getRetryWaitTime() {
2753
                return retryWaitTime;
1✔
2754
        }
2755

2756
        /**
2757
         * リトライタイムアウト時間(ms) を設定します
2758
         *
2759
         * @param retryWaitTime リトライタイムアウト時間(ms)
2760
         * @return SqlAgent
2761
         */
2762
        public SqlAgent setRetryWaitTime(final int retryWaitTime) {
2763
                this.retryWaitTime = retryWaitTime;
1✔
2764
                return this;
1✔
2765
        }
2766

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

2778
        /**
2779
         *
2780
         * {@inheritDoc}
2781
         *
2782
         * @see jp.co.future.uroborosql.SqlAgent#setMapKeyCaseFormat(jp.co.future.uroborosql.utils.CaseFormat)
2783
         */
2784
        @Override
2785
        public SqlAgent setMapKeyCaseFormat(final CaseFormat mapKeyCaseFormat) {
2786
                this.mapKeyCaseFormat = mapKeyCaseFormat;
1✔
2787
                return this;
1✔
2788
        }
2789

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

2801
        /**
2802
         *
2803
         * {@inheritDoc}
2804
         *
2805
         * @see jp.co.future.uroborosql.SqlAgent#setInsertsType(jp.co.future.uroborosql.enums.InsertsType)
2806
         */
2807
        @Override
2808
        public SqlAgent setInsertsType(final InsertsType defaultInsertsType) {
2809
                this.insertsType = defaultInsertsType;
1✔
2810
                return this;
1✔
2811
        }
2812

2813
        /**
2814
         * SQLリソース管理クラスを取得します。
2815
         *
2816
         * @return SQLリソース管理クラス
2817
         */
2818
        private SqlResourceManager getSqlResourceManager() {
2819
                return sqlConfig.getSqlResourceManager();
1✔
2820
        }
2821

2822
        /**
2823
         * ORM処理クラス を取得します。
2824
         *
2825
         * @return ORM処理クラス
2826
         */
2827
        @SuppressWarnings("unchecked")
2828
        private <E> EntityHandler<E> getEntityHandler() {
2829
                return (EntityHandler<E>) sqlConfig.getEntityHandler();
1✔
2830
        }
2831

2832
        /**
2833
         * Dialect を取得します。
2834
         *
2835
         * @return Dialect
2836
         */
2837
        private Dialect getDialect() {
2838
                return sqlConfig.getDialect();
1✔
2839
        }
2840

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

2855
        /**
2856
         * Callableステートメント初期化
2857
         *
2858
         * @param executionContext ExecutionContext
2859
         * @return CallableStatement
2860
         * @throws SQLException SQL例外
2861
         */
2862
        private CallableStatement getCallableStatement(final ExecutionContext executionContext) throws SQLException {
2863
                var stmt = ((LocalTransactionManager) transactionManager).getCallableStatement(executionContext);
1✔
2864
                // プロパティ設定
2865
                applyProperties(stmt);
1✔
2866
                return stmt;
1✔
2867
        }
2868

2869
        /**
2870
         * フェッチサイズとクエリタイムアウトをPreparedStatementに設定する
2871
         *
2872
         * @param preparedStatement PreparedStatement
2873
         * @throws SQLException SQL例外
2874
         */
2875
        private void applyProperties(final PreparedStatement preparedStatement) throws SQLException {
2876
                // フェッチサイズ指定
2877
                if (getFetchSize() >= 0 && !(preparedStatement instanceof CallableStatement)) {
1✔
2878
                        preparedStatement.setFetchSize(getFetchSize());
1✔
2879
                }
2880

2881
                // クエリタイムアウト指定
2882
                if (getQueryTimeout() >= 0) {
1✔
2883
                        preparedStatement.setQueryTimeout(getQueryTimeout());
1✔
2884
                }
2885
        }
1✔
2886

2887
        /**
2888
         * 経過時間を計算し、HH:mm:ss.SSSSSSにフォーマットする.
2889
         *
2890
         * @param start 開始時間
2891
         * @param end 終了時間
2892
         * @return フォーマットした経過時間
2893
         */
2894
        private static String formatElapsedTime(final Instant start, final Instant end) {
2895
                return ELAPSED_TIME_FORMAT.format(LocalTime.MIDNIGHT.plus(Duration.between(start, end)));
1✔
2896
        }
2897

2898
        /**
2899
         * ResultSetをStreamで扱うためのSpliterator
2900
         *
2901
         * @author H.Sugimoto
2902
         *
2903
         * @param <T> ResultSetの1行を変換した型
2904
         */
2905
        private static final class ResultSetSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
2906
                private final ResultSetConverter<T> converter;
2907
                private final ResultSet rs;
2908
                private boolean finished = false;
1✔
2909

2910
                private ResultSetSpliterator(final ResultSet rs, final ResultSetConverter<T> converter) {
2911
                        super(Long.MAX_VALUE, Spliterator.ORDERED);
1✔
2912
                        this.rs = rs;
1✔
2913
                        this.converter = converter;
1✔
2914
                }
1✔
2915

2916
                @Override
2917
                public boolean tryAdvance(final Consumer<? super T> action) {
2918
                        try {
2919
                                if (finished || !rs.next()) {
1✔
2920
                                        if (!rs.isClosed()) {
1✔
2921
                                                rs.close();
1✔
2922
                                        }
2923
                                        finished = true;
1✔
2924
                                        return false;
1✔
2925
                                }
2926
                                action.accept(converter.createRecord(rs));
1✔
2927
                                return true;
1✔
2928
                        } catch (RuntimeException | Error ex) {
1✔
2929
                                try {
2930
                                        if (rs != null && !rs.isClosed()) {
1✔
2931
                                                rs.close();
1✔
2932
                                        }
2933
                                } catch (SQLException ex2) {
×
2934
                                        LOG.error(ex2.getMessage(), ex2);
×
2935
                                }
1✔
2936
                                throw ex;
1✔
2937
                        } catch (SQLException ex) {
×
2938
                                try {
2939
                                        if (rs != null && !rs.isClosed()) {
×
2940
                                                rs.close();
×
2941
                                        }
2942
                                } catch (SQLException ex2) {
×
2943
                                        LOG.error(ex2.getMessage(), ex2);
×
2944
                                }
×
2945
                                throw new UroborosqlSQLException(ex);
×
2946
                        }
2947
                }
2948
        }
2949

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

2960
                /**
2961
                 * コンストラクタ
2962
                 *
2963
                 * @param wrapped 元となるResultSet
2964
                 * @param stmt Statement
2965
                 */
2966
                InnerResultSet(final ResultSet wrapped, final Statement stmt) {
2967
                        super(wrapped);
1✔
2968
                        this.stmt = stmt;
1✔
2969
                }
1✔
2970

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