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

future-architect / uroborosql / #745

15 Jul 2024 09:53AM UTC coverage: 91.32% (+0.3%) from 90.988%
#745

Pull #311

HidekiSugimoto189
fix javadoc verify error.
Pull Request #311: refactoring api

567 of 579 new or added lines in 23 files covered. (97.93%)

2 existing lines in 1 file now uncovered.

8743 of 9574 relevant lines covered (91.32%)

0.91 hits per line

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

96.59
/src/main/java/jp/co/future/uroborosql/mapping/DefaultEntityHandler.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.mapping;
8

9
import java.sql.JDBCType;
10
import java.sql.SQLException;
11
import java.util.ArrayList;
12
import java.util.List;
13
import java.util.Map;
14
import java.util.Objects;
15
import java.util.function.Function;
16
import java.util.stream.IntStream;
17
import java.util.stream.Stream;
18

19
import jp.co.future.uroborosql.SqlAgent;
20
import jp.co.future.uroborosql.config.SqlConfig;
21
import jp.co.future.uroborosql.connection.ConnectionManager;
22
import jp.co.future.uroborosql.context.ExecutionContext;
23
import jp.co.future.uroborosql.converter.EntityResultSetConverter;
24
import jp.co.future.uroborosql.enums.GenerationType;
25
import jp.co.future.uroborosql.enums.SqlKind;
26
import jp.co.future.uroborosql.exception.UroborosqlSQLException;
27
import jp.co.future.uroborosql.mapping.TableMetadata.Column;
28
import jp.co.future.uroborosql.mapping.mapper.PropertyMapper;
29
import jp.co.future.uroborosql.mapping.mapper.PropertyMapperManager;
30
import jp.co.future.uroborosql.utils.CaseFormat;
31
import jp.co.future.uroborosql.utils.ObjectUtils;
32

33
/**
34
 * デフォルトORM処理クラス
35
 *
36
 * @author ota
37
 */
38
public class DefaultEntityHandler implements EntityHandler<Object> {
39
        /** TableMetadataのキャッシュサイズ. */
40
        protected static final int CACHE_SIZE = Integer.getInteger("uroborosql.entity.cache.size", 30);
1✔
41
        /** TableMetadataのLRUキャッシュ. */
42
        protected static final ConcurrentLruCache<String, TableMetadata> CACHE = new ConcurrentLruCache<>(CACHE_SIZE);
1✔
43
        /** プロパティマッパーマネージャー. */
44
        protected PropertyMapperManager propertyMapperManager;
45
        /** 空文字をNULLとして扱うか. */
46
        protected boolean emptyStringEqualsNull = true;
1✔
47
        /** SQLコンフィグ. */
48
        protected SqlConfig sqlConfig = null;
1✔
49

50
        /**
51
         * コンストラクタ
52
         */
53
        public DefaultEntityHandler() {
1✔
54
        }
1✔
55

56
        /**
57
         * コンストラクタ
58
         * @param emptyStringEqualsNull 空文字をNULLとして扱うか
59
         */
NEW
60
        public DefaultEntityHandler(final boolean emptyStringEqualsNull) {
×
NEW
61
                this.emptyStringEqualsNull = emptyStringEqualsNull;
×
NEW
62
        }
×
63

64
        /**
65
         * {@inheritDoc}
66
         *
67
         * @see jp.co.future.uroborosql.mapping.EntityHandler#isEmptyStringEqualsNull()
68
         */
69
        @Override
70
        public boolean isEmptyStringEqualsNull() {
71
                return emptyStringEqualsNull;
×
72
        }
73

74
        /**
75
         * {@inheritDoc}
76
         *
77
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setEmptyStringEqualsNull(boolean)
78
         */
79
        @Override
80
        public EntityHandler<Object> setEmptyStringEqualsNull(final boolean emptyStringEqualsNull) {
81
                this.emptyStringEqualsNull = emptyStringEqualsNull;
1✔
82
                return this;
1✔
83
        }
84

85
        /**
86
         * {@inheritDoc}
87
         *
88
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createSelectContext(SqlAgent, TableMetadata, Class, boolean)
89
         */
90
        @Override
91
        public ExecutionContext createSelectContext(final SqlAgent agent, final TableMetadata metadata,
92
                        final Class<? extends Object> entityType, final boolean addCondition) {
93
                return agent.context().setSql(buildSelectSQL(metadata, entityType, addCondition))
1✔
94
                                .setSqlId(createSqlId(metadata, entityType))
1✔
95
                                .setSchema(metadata.getSchema())
1✔
96
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
97
        }
98

99
        /**
100
         * {@inheritDoc}
101
         *
102
         * @see jp.co.future.uroborosql.mapping.EntityHandler#doSelect(SqlAgent, ExecutionContext, Class)
103
         */
104
        @Override
105
        public <E> Stream<E> doSelect(final SqlAgent agent, final ExecutionContext context,
106
                        final Class<? extends E> entityType) throws SQLException {
107
                return agent.query(context,
1✔
108
                                new EntityResultSetConverter<>(context.getSchema(), entityType, propertyMapperManager));
1✔
109
        }
110

111
        /**
112
         * {@inheritDoc}
113
         *
114
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createInsertContext(SqlAgent, TableMetadata, Class)
115
         */
116
        @Override
117
        public ExecutionContext createInsertContext(final SqlAgent agent, final TableMetadata metadata,
118
                        final Class<? extends Object> entityType) {
119
                return agent.context().setSql(buildInsertSQL(metadata, entityType))
1✔
120
                                .setSqlId(createSqlId(metadata, entityType))
1✔
121
                                .setSchema(metadata.getSchema())
1✔
122
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
123
        }
124

125
        /**
126
         * {@inheritDoc}
127
         *
128
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createUpdateContext(SqlAgent, TableMetadata, Class, boolean)
129
         */
130
        @Override
131
        public ExecutionContext createUpdateContext(final SqlAgent agent, final TableMetadata metadata,
132
                        final Class<? extends Object> entityType, final boolean addCondition) {
133
                return agent.context().setSql(buildUpdateSQL(metadata, entityType, addCondition, true))
1✔
134
                                .setSqlId(createSqlId(metadata, entityType))
1✔
135
                                .setSchema(metadata.getSchema())
1✔
136
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
137
        }
138

139
        /**
140
         * {@inheritDoc}
141
         *
142
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createDeleteContext(SqlAgent, TableMetadata, Class, boolean)
143
         */
144
        @Override
145
        public ExecutionContext createDeleteContext(final SqlAgent agent, final TableMetadata metadata,
146
                        final Class<? extends Object> entityType, final boolean addCondition) {
147
                return agent.context().setSql(buildDeleteSQL(metadata, entityType, addCondition))
1✔
148
                                .setSqlId(createSqlId(metadata, entityType))
1✔
149
                                .setSchema(metadata.getSchema())
1✔
150
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
151
        }
152

153
        /**
154
         * {@inheritDoc}
155
         *
156
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createBatchInsertContext(SqlAgent, TableMetadata, Class)
157
         */
158
        @Override
159
        public ExecutionContext createBatchInsertContext(final SqlAgent agent, final TableMetadata metadata,
160
                        final Class<? extends Object> entityType) {
161
                return agent.context().setSql(buildInsertSQL(metadata, entityType, false))
1✔
162
                                .setSqlId(createSqlId(metadata, entityType))
1✔
163
                                .setSchema(metadata.getSchema())
1✔
164
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
165
        }
166

167
        /**
168
         * {@inheritDoc}
169
         *
170
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createBulkInsertContext(SqlAgent, TableMetadata, Class)
171
         */
172
        @Override
173
        public ExecutionContext createBulkInsertContext(final SqlAgent agent, final TableMetadata metadata,
174
                        final Class<? extends Object> entityType) {
175
                return agent.context()
1✔
176
                                .setSqlId(createSqlId(metadata, entityType))
1✔
177
                                .setSchema(metadata.getSchema())
1✔
178
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
179
        }
180

181
        /**
182
         * {@inheritDoc}
183
         *
184
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createBatchUpdateContext(SqlAgent, TableMetadata, Class)
185
         */
186
        @Override
187
        public ExecutionContext createBatchUpdateContext(final SqlAgent agent, final TableMetadata metadata,
188
                        final Class<? extends Object> entityType) {
189
                return agent.context().setSql(buildUpdateSQL(metadata, entityType, true, false))
1✔
190
                                .setSqlId(createSqlId(metadata, entityType))
1✔
191
                                .setSchema(metadata.getSchema())
1✔
192
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
193
        }
194

195
        /**
196
         * {@inheritDoc}
197
         *
198
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setupSqlBulkInsertContext(SqlAgent, ExecutionContext, TableMetadata, Class, int)
199
         */
200
        @Override
201
        public ExecutionContext setupSqlBulkInsertContext(final SqlAgent agent, final ExecutionContext context,
202
                        final TableMetadata metadata, final Class<? extends Object> entityType, final int numberOfRecords) {
203
                return context.setSql(buildBulkInsertSQL(metadata, entityType, numberOfRecords))
1✔
204
                                .setSchema(metadata.getSchema())
1✔
205
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
206
        }
207

208
        /**
209
         * {@inheritDoc}
210
         *
211
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setInsertParams(ExecutionContext, Object)
212
         */
213
        @Override
214
        public void setInsertParams(final ExecutionContext context, final Object entity) {
215
                setFields(context, entity, SqlKind.INSERT, MappingColumn::getCamelName);
1✔
216
        }
1✔
217

218
        /**
219
         * {@inheritDoc}
220
         *
221
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setUpdateParams(ExecutionContext, Object)
222
         */
223
        @Override
224
        public void setUpdateParams(final ExecutionContext context, final Object entity) {
225
                setFields(context, entity, SqlKind.UPDATE, MappingColumn::getCamelName);
1✔
226
        }
1✔
227

228
        /**
229
         * {@inheritDoc}
230
         *
231
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setDeleteParams(ExecutionContext, Object)
232
         */
233
        @Override
234
        public void setDeleteParams(final ExecutionContext context, final Object entity) {
235
                setFields(context, entity, SqlKind.DELETE, MappingColumn::getCamelName);
1✔
236
        }
1✔
237

238
        /**
239
         * {@inheritDoc}
240
         *
241
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setBulkInsertParams(ExecutionContext, Object, int)
242
         */
243
        @Override
244
        public void setBulkInsertParams(final ExecutionContext context, final Object entity, final int entityIndex) {
245
                setFields(context, entity, SqlKind.INSERT, col -> buildBulkParamName(col.getCamelName(), entityIndex));
1✔
246
        }
1✔
247

248
        /**
249
         * {@inheritDoc}
250
         *
251
         * @see jp.co.future.uroborosql.mapping.EntityHandler#getEntityType()
252
         */
253
        @Override
254
        public Class<Object> getEntityType() {
255
                return Object.class;
1✔
256
        }
257

258
        @Override
259
        public TableMetadata getMetadata(final ConnectionManager connectionManager, final Class<?> entityType)
260
                        throws SQLException {
261
                var cacheKey = getCacheKey(connectionManager, entityType);
1✔
262
                return CACHE.get(cacheKey, key -> {
1✔
263
                        try {
264
                                return createMetadata(connectionManager, entityType);
1✔
265
                        } catch (SQLException ex) {
×
266
                                throw new UroborosqlSQLException(ex);
×
267
                        }
268
                });
269
        }
270

271
        private String getCacheKey(final ConnectionManager connectionManager, final Class<?> entityType) {
272
                try {
273
                        var table = MappingUtils.getTable(entityType);
1✔
274
                        var schema = ObjectUtils.isNotEmpty(table.getSchema()) ? table.getSchema()
1✔
275
                                        : Objects.toString(connectionManager.getConnection().getSchema(), "");
1✔
276
                        return String.format("%s.%s", schema.toUpperCase(), entityType.getName());
1✔
277
                } catch (SQLException ex) {
×
278
                        return entityType.getName();
×
279
                }
280
        }
281

282
        /**
283
         * {@inheritDoc}
284
         *
285
         * @see jp.co.future.uroborosql.mapping.EntityHandler#addPropertyMapper(jp.co.future.uroborosql.mapping.mapper.PropertyMapper)
286
         */
287
        @Override
288
        public EntityHandler<Object> addPropertyMapper(final PropertyMapper<?> propertyMapper) {
289
                propertyMapperManager.addMapper(propertyMapper);
1✔
290
                return this;
1✔
291
        }
292

293
        /**
294
         * {@inheritDoc}
295
         *
296
         * @see jp.co.future.uroborosql.mapping.EntityHandler#removePropertyMapper(jp.co.future.uroborosql.mapping.mapper.PropertyMapper)
297
         */
298
        @Override
299
        public EntityHandler<Object> removePropertyMapper(final PropertyMapper<?> propertyMapper) {
300
                propertyMapperManager.removeMapper(propertyMapper);
1✔
301
                return this;
1✔
302
        }
303

304
        /**
305
         * エンティティ型から、EntityMetadataの生成
306
         *
307
         * @param connectionManager コネクションマネージャー
308
         * @param type エンティティ型
309
         * @return TableMetadata
310
         * @throws SQLException SQL例外
311
         */
312
        protected TableMetadata createMetadata(final ConnectionManager connectionManager,
313
                        final Class<? extends Object> type) throws SQLException {
314
                var table = getTable(type);
1✔
315
                return TableMetadata.createTableEntityMetadata(connectionManager, table);
1✔
316
        }
317

318
        /**
319
         * エンティティ型からテーブル情報の取得
320
         *
321
         * @param type エンティティ型
322
         * @return テーブル情報
323
         */
324
        protected Table getTable(final Class<? extends Object> type) {
325
                return MappingUtils.getTable(type);
1✔
326
        }
327

328
        /**
329
         * SELECT SQL生成
330
         *
331
         * @param metadata エンティティメタ情報
332
         * @param type エイティティタイプ
333
         * @param addCondition 条件を追加するかどうか。追加する場合<code>true</code>
334
         * @return SELECT SQL
335
         */
336
        protected String buildSelectSQL(final TableMetadata metadata, final Class<? extends Object> type,
337
                        final boolean addCondition) {
338
                var columns = metadata.getColumns();
1✔
339

340
                var sql = new StringBuilder(
1✔
341
                                buildSelectClause(metadata, type, getSqlConfig().getSqlAgentProvider().getSqlIdKeyName()));
1✔
342

343
                if (addCondition) {
1✔
344
                        sql.append("/*BEGIN*/").append(System.lineSeparator());
1✔
345
                        sql.append("WHERE").append(System.lineSeparator());
1✔
346

347
                        for (var col : columns) {
1✔
348
                                var camelColName = col.getCamelColumnName();
1✔
349
                                var parts = new StringBuilder().append("\t").append("AND ")
1✔
350
                                                .append(col.getColumnIdentifier())
1✔
351
                                                .append(" = ").append("/*").append(camelColName).append("*/''").append(System.lineSeparator());
1✔
352
                                wrapIfComment(sql, parts, col);
1✔
353
                        }
1✔
354
                        sql.append("/*END*/").append(System.lineSeparator());
1✔
355

356
                        var firstFlag = true;
1✔
357
                        var keys = metadata.getKeyColumns();
1✔
358
                        if (!keys.isEmpty()) {
1✔
359
                                sql.append("ORDER BY").append(System.lineSeparator());
1✔
360
                                firstFlag = true;
1✔
361
                                for (var col : keys) {
1✔
362
                                        sql.append("\t");
1✔
363
                                        if (firstFlag) {
1✔
364
                                                sql.append("  ");
1✔
365
                                                firstFlag = false;
1✔
366
                                        } else {
367
                                                sql.append(", ");
1✔
368
                                        }
369
                                        sql.append(col.getColumnIdentifier()).append(System.lineSeparator());
1✔
370
                                }
1✔
371
                        }
372
                }
373

374
                return sql.toString();
1✔
375
        }
376

377
        /**
378
         * SELECT句生成
379
         *
380
         * @param metadata エンティティメタ情報
381
         * @param type エイティティタイプ
382
         * @param sqlIdKeyName SQL_IDキー名
383
         * @return SELECT句
384
         */
385
        protected String buildSelectClause(final TableMetadata metadata, final Class<? extends Object> type,
386
                        final String sqlIdKeyName) {
387
                var columns = metadata.getColumns();
1✔
388

389
                var sql = new StringBuilder("SELECT ").append("/* ").append(sqlIdKeyName).append(" */")
1✔
390
                                .append(System.lineSeparator());
1✔
391

392
                var firstFlag = true;
1✔
393
                for (var col : columns) {
1✔
394
                        sql.append("\t");
1✔
395
                        if (firstFlag) {
1✔
396
                                sql.append("  ");
1✔
397
                                firstFlag = false;
1✔
398
                        } else {
399
                                sql.append(", ");
1✔
400
                        }
401
                        sql.append(col.getColumnIdentifier()).append("\tAS\t").append(col.getColumnIdentifier());
1✔
402
                        if (ObjectUtils.isNotEmpty(col.getRemarks())) {
1✔
403
                                sql.append("\t").append("-- ").append(col.getRemarks());
1✔
404
                        }
405
                        sql.append(System.lineSeparator());
1✔
406
                }
1✔
407
                sql.append("FROM ").append(metadata.getTableIdentifier()).append(System.lineSeparator());
1✔
408

409
                return sql.toString();
1✔
410
        }
411

412
        /**
413
         * INSERT SQL生成
414
         *
415
         * @param metadata エンティティメタ情報
416
         * @param type エイティティタイプ
417
         * @return INSERT SQL
418
         */
419
        protected String buildInsertSQL(final TableMetadata metadata, final Class<? extends Object> type) {
420
                return buildInsertSQL(metadata, type, true);
1✔
421
        }
422

423
        /**
424
         * INSERT SQL生成
425
         *
426
         * @param metadata エンティティメタ情報
427
         * @param type エイティティタイプ
428
         * @param ignoreWhenEmpty 空のパラメータをSQLに含めない条件文を設定する
429
         * @return INSERT SQL
430
         */
431
        protected String buildInsertSQL(final TableMetadata metadata, final Class<? extends Object> type,
432
                        final boolean ignoreWhenEmpty) {
433
                var mappingColumns = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.INSERT);
1✔
434
                var sql = buildInsertTargetBlock(metadata, mappingColumns, ignoreWhenEmpty);
1✔
435
                sql.append(" VALUES ");
1✔
436
                sql.append(buildInsertRowBlock(metadata, mappingColumns, ignoreWhenEmpty,
1✔
437
                                TableMetadata.Column::getCamelColumnName));
438
                return sql.toString();
1✔
439
        }
440

441
        /**
442
         * BULK INSERT SQL生成
443
         *
444
         * @param metadata エンティティメタ情報
445
         * @param type エイティティタイプ
446
         * @param numberOfRecords レコード行数
447
         * @return INSERT SQL
448
         */
449
        protected String buildBulkInsertSQL(final TableMetadata metadata, final Class<? extends Object> type,
450
                        final int numberOfRecords) {
451
                var mappingColumns = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.INSERT);
1✔
452
                var sql = buildInsertTargetBlock(metadata, mappingColumns, false);
1✔
453
                sql.append(" VALUES ");
1✔
454

455
                IntStream.range(0, numberOfRecords).forEach(i -> {
1✔
456
                        if (i > 0) {
1✔
457
                                sql.append(",").append(System.lineSeparator());
1✔
458
                        }
459
                        sql.append(buildInsertRowBlock(metadata, mappingColumns, false,
1✔
460
                                        col -> buildBulkParamName(col.getCamelColumnName(), i)));
1✔
461
                });
1✔
462
                return sql.toString();
1✔
463
        }
464

465
        /**
466
         * UPDATE SQL生成
467
         *
468
         * @param metadata エンティティメタ情報
469
         * @param type エイティティタイプ
470
         * @param addCondition 条件を追加するかどうか。追加する場合<code>true</code>
471
         * @param ignoreWhenEmpty 空のパラメータをSQLに含めない条件文を設定する
472
         * @return UPDATE SQL
473
         */
474
        protected String buildUpdateSQL(final TableMetadata metadata, final Class<? extends Object> type,
475
                        final boolean addCondition, final boolean ignoreWhenEmpty) {
476
                var sql = new StringBuilder("UPDATE ").append("/* ")
1✔
477
                                .append(getSqlConfig().getSqlAgentProvider().getSqlIdKeyName()).append(" */")
1✔
478
                                .append(" ").append(metadata.getTableIdentifier()).append(" SET ").append(System.lineSeparator());
1✔
479

480
                var mappingColumns = MappingUtils.getMappingColumnMap(metadata.getSchema(), type, SqlKind.UPDATE);
1✔
481

482
                String versionColumnName = null;
1✔
483
                OptimisticLockSupplier optimisticLockSupplier = null;
1✔
484
                if (type == null) {
1✔
485
                        TableMetadata.Column versionColumn = metadata.getColumns().stream().filter(Column::isVersion).findFirst()
1✔
486
                                        .orElse(null);
1✔
487
                        if (versionColumn != null) {
1✔
488
                                versionColumnName = versionColumn.getCamelColumnName();
1✔
489
                                optimisticLockSupplier = OptimisticLockSupplier.getSupplier(versionColumn.getOptimisticLockType());
1✔
490
                        }
491
                } else {
1✔
492
                        var versionColumn = MappingUtils.getVersionMappingColumn(metadata.getSchema(), type);
1✔
493
                        versionColumnName = versionColumn.map(MappingColumn::getCamelName).orElse(null);
1✔
494
                        optimisticLockSupplier = versionColumn
1✔
495
                                        .map(vc -> OptimisticLockSupplier.getSupplier(vc.getVersion().supplier())).orElse(null);
1✔
496
                }
497

498
                var firstFlag = true;
1✔
499
                for (var col : metadata.getColumns()) {
1✔
500
                        var mappingColumn = mappingColumns.get(col.getCamelColumnName());
1✔
501
                        var autoIncrementColumn = mappingColumn != null && mappingColumn.isId() || col.isAutoincrement();
1✔
502

503
                        if (!mappingColumns.isEmpty() && mappingColumn == null || addCondition && autoIncrementColumn) {
1✔
504
                                // Transient annotation のついているカラムはスキップ、または、WHERE条件を追加する場合、自動採番カラムは更新対象としないためスキップ
505
                                continue;
1✔
506
                        }
507

508
                        var camelColName = col.getCamelColumnName();
1✔
509
                        var parts = new StringBuilder().append("\t");
1✔
510
                        if (firstFlag) {
1✔
511
                                if (col.isNullable() || autoIncrementColumn) {
1✔
512
                                        parts.append(", ");
1✔
513
                                } else {
514
                                        parts.append("  ");
1✔
515
                                }
516
                                firstFlag = false;
1✔
517
                        } else {
518
                                parts.append(", ");
1✔
519
                        }
520

521
                        var isVersionColumn = camelColName.equalsIgnoreCase(versionColumnName);
1✔
522
                        if (isVersionColumn) {
1✔
523
                                parts.append(optimisticLockSupplier.getPart(col, getSqlConfig()));
1✔
524
                        } else {
525
                                parts.append(col.getColumnIdentifier());
1✔
526
                                parts.append(" = /*").append(camelColName).append("*/''");
1✔
527
                        }
528
                        if (ObjectUtils.isNotEmpty(col.getRemarks())) {
1✔
529
                                parts.append("\t").append("-- ").append(col.getRemarks());
1✔
530
                        }
531
                        parts.append(System.lineSeparator());
1✔
532
                        if (isVersionColumn) {
1✔
533
                                sql.append(parts);
1✔
534
                        } else if (addCondition) {
1✔
535
                                if (ignoreWhenEmpty && col.isNullable()) {
1✔
536
                                        wrapIfComment(sql, parts, col);
1✔
537
                                } else {
538
                                        sql.append(parts);
1✔
539
                                }
540
                        } else {
541
                                if (ignoreWhenEmpty || autoIncrementColumn) {
1✔
542
                                        wrapIfComment(sql, parts, col);
1✔
543
                                } else {
544
                                        sql.append(parts);
×
545
                                }
546
                        }
547
                }
1✔
548

549
                if (addCondition) {
1✔
550
                        sql.append("WHERE").append(System.lineSeparator());
1✔
551
                        var cols = !metadata.getKeyColumns().isEmpty() ? metadata.getKeyColumns()
1✔
552
                                        : List.of(metadata.getColumns().get(0));
1✔
553
                        firstFlag = true;
1✔
554
                        for (final TableMetadata.Column col : cols) {
1✔
555
                                var parts = new StringBuilder().append("\t");
1✔
556
                                if (firstFlag) {
1✔
557
                                        if (col.isNullable()) {
1✔
558
                                                parts.append("AND ");
1✔
559
                                        } else {
560
                                                parts.append("    ");
1✔
561
                                        }
562
                                        firstFlag = false;
1✔
563
                                } else {
564
                                        parts.append("AND ");
1✔
565
                                }
566
                                parts.append(col.getColumnIdentifier()).append(" = ").append("/*").append(col.getCamelColumnName())
1✔
567
                                                .append("*/''")
1✔
568
                                                .append(System.lineSeparator());
1✔
569
                                if (ignoreWhenEmpty && col.isNullable()) {
1✔
570
                                        wrapIfComment(sql, parts, col);
1✔
571
                                } else {
572
                                        sql.append(parts);
1✔
573
                                }
574
                        }
1✔
575
                        var first = firstFlag;
1✔
576
                        if (versionColumnName != null) {
1✔
577
                                var col = metadata.getColumn(versionColumnName);
1✔
578
                                sql.append("\t");
1✔
579
                                if (first) {
1✔
580
                                        sql.append("    ");
×
581
                                } else {
582
                                        sql.append("AND ");
1✔
583
                                }
584
                                sql.append(col.getColumnIdentifier()).append(" = ").append("/*").append(col.getCamelColumnName())
1✔
585
                                                .append("*/''").append(System.lineSeparator());
1✔
586
                        }
587
                }
588
                return sql.toString();
1✔
589
        }
590

591
        /**
592
         * DELETE SQL生成
593
         *
594
         * @param metadata エンティティメタ情報
595
         * @param type エイティティタイプ
596
         * @param addCondition 条件を追加するかどうか。追加する場合<code>true</code>
597
         * @return DELETE SQL
598
         */
599
        protected String buildDeleteSQL(final TableMetadata metadata, final Class<? extends Object> type,
600
                        final boolean addCondition) {
601
                var sql = new StringBuilder("DELETE ").append("/* ")
1✔
602
                                .append(getSqlConfig().getSqlAgentProvider().getSqlIdKeyName()).append(" */")
1✔
603
                                .append(" FROM ").append(metadata.getTableIdentifier()).append("").append(System.lineSeparator());
1✔
604

605
                if (addCondition) {
1✔
606
                        var firstFlag = true;
1✔
607
                        sql.append("WHERE").append(System.lineSeparator());
1✔
608

609
                        var cols = !metadata.getKeyColumns().isEmpty() ? metadata.getKeyColumns()
1✔
610
                                        : List.of(metadata.getColumns().get(0));
1✔
611
                        for (var col : cols) {
1✔
612
                                var parts = new StringBuilder().append("\t");
1✔
613
                                if (firstFlag) {
1✔
614
                                        if (col.isNullable()) {
1✔
615
                                                parts.append("AND ");
1✔
616
                                        } else {
617
                                                parts.append("    ");
1✔
618
                                        }
619
                                        firstFlag = false;
1✔
620
                                } else {
621
                                        parts.append("AND ");
×
622
                                }
623
                                parts.append(col.getColumnIdentifier()).append(" = ").append("/*").append(col.getCamelColumnName())
1✔
624
                                                .append("*/''").append(System.lineSeparator());
1✔
625
                                if (col.isNullable()) {
1✔
626
                                        wrapIfComment(sql, parts, col);
1✔
627
                                } else {
628
                                        sql.append(parts);
1✔
629
                                }
630
                        }
1✔
631
                }
632
                return sql.toString();
1✔
633
        }
634

635
        /**
636
         * INSERT文の中で 挿入対象カラムを構成するブロックを生成する.
637
         *
638
         * @param metadata エンティティメタ情報
639
         * @param mappingColumns マッピングカラム情報
640
         * @param ignoreWhenEmpty 空の場合無視するかどうか
641
         * @return 生成したSQLパーツ
642
         */
643
        protected StringBuilder buildInsertTargetBlock(final TableMetadata metadata,
644
                        final Map<String, MappingColumn> mappingColumns,
645
                        final boolean ignoreWhenEmpty) {
646

647
                var sql = new StringBuilder("INSERT ").append("/* ")
1✔
648
                                .append(getSqlConfig().getSqlAgentProvider().getSqlIdKeyName()).append(" */")
1✔
649
                                .append(" INTO ").append(metadata.getTableIdentifier()).append(" (").append(System.lineSeparator());
1✔
650

651
                var firstFlag = true;
1✔
652
                for (var col : metadata.getColumns()) {
1✔
653
                        var mappingColumn = mappingColumns.get(col.getCamelColumnName());
1✔
654
                        if (!mappingColumns.isEmpty() && mappingColumn == null) {
1✔
655
                                // Transient annotation のついているカラムをスキップ
656
                                continue;
1✔
657
                        }
658
                        var autoIncrementColumn = mappingColumn != null && mappingColumn.isId()
1✔
659
                                        && GenerationType.IDENTITY.equals(mappingColumn.getGeneratedValue().strategy()) ||
1✔
660
                                        col.isAutoincrement();
1✔
661

662
                        var parts = new StringBuilder().append("\t");
1✔
663
                        if (firstFlag) {
1✔
664
                                if (col.isNullable() || autoIncrementColumn) {
1✔
665
                                        parts.append(", ");
1✔
666
                                } else {
667
                                        parts.append("  ");
1✔
668
                                }
669
                                firstFlag = false;
1✔
670
                        } else {
671
                                parts.append(", ");
1✔
672
                        }
673

674
                        parts.append(col.getColumnIdentifier());
1✔
675
                        if (ObjectUtils.isNotEmpty(col.getRemarks())) {
1✔
676
                                parts.append("\t").append("-- ").append(col.getRemarks());
1✔
677
                        }
678
                        parts.append(System.lineSeparator());
1✔
679
                        if (ignoreWhenEmpty && (col.isNullable() || ObjectUtils.isNotEmpty(col.getColumnDefault()))
1✔
680
                                        || autoIncrementColumn) {
681
                                wrapIfComment(sql, parts, col);
1✔
682
                        } else {
683
                                sql.append(parts);
1✔
684
                        }
685
                }
1✔
686

687
                sql.append(")");
1✔
688
                return sql;
1✔
689
        }
690

691
        /**
692
         * INSERT文の中で、挿入する値を構成するブロックを生成する.
693
         *
694
         * @param metadata エンティティメタ情報
695
         * @param mappingColumns マッピングカラム情報
696
         * @param ignoreWhenEmpty 空の場合無視するかどうか
697
         * @param getParamName パラメータ名取得関数
698
         * @return 生成したSQLパーツ
699
         */
700
        protected StringBuilder buildInsertRowBlock(final TableMetadata metadata,
701
                        final Map<String, MappingColumn> mappingColumns,
702
                        final boolean ignoreWhenEmpty, final Function<TableMetadata.Column, String> getParamName) {
703
                var sql = new StringBuilder("(").append(System.lineSeparator());
1✔
704
                var firstFlag = true;
1✔
705
                for (var col : metadata.getColumns()) {
1✔
706
                        var mappingColumn = mappingColumns.get(col.getCamelColumnName());
1✔
707

708
                        if (!mappingColumns.isEmpty() && mappingColumn == null) {
1✔
709
                                // Transient annotation のついているカラムをスキップ
710
                                continue;
1✔
711
                        }
712
                        var autoIncrementColumn = mappingColumn != null && mappingColumn.isId()
1✔
713
                                        && GenerationType.IDENTITY.equals(mappingColumn.getGeneratedValue().strategy()) ||
1✔
714
                                        col.isAutoincrement();
1✔
715

716
                        var parts = new StringBuilder().append("\t");
1✔
717
                        if (firstFlag) {
1✔
718
                                if (col.isNullable() || autoIncrementColumn) {
1✔
719
                                        parts.append(", ");
1✔
720
                                } else {
721
                                        parts.append("  ");
1✔
722
                                }
723
                                firstFlag = false;
1✔
724
                        } else {
725
                                parts.append(", ");
1✔
726
                        }
727

728
                        if (mappingColumn != null && mappingColumn.isId()
1✔
729
                                        && GenerationType.SEQUENCE.equals(mappingColumn.getGeneratedValue().strategy())) {
1✔
730
                                var sequenceName = mappingColumn.getQualifiedSequenceName();
1✔
731
                                parts.append(getSqlConfig().getDialect().getSequenceNextValSql(sequenceName))
1✔
732
                                                .append(System.lineSeparator());
1✔
733
                                sql.append(parts);
1✔
734
                        } else {
1✔
735
                                parts.append("/*").append(getParamName.apply(col)).append("*/''").append(System.lineSeparator());
1✔
736
                                if (ignoreWhenEmpty && (col.isNullable() || ObjectUtils.isNotEmpty(col.getColumnDefault()))
1✔
737
                                                || autoIncrementColumn) {
738
                                        wrapIfComment(sql, parts, col);
1✔
739
                                } else {
740
                                        sql.append(parts);
1✔
741
                                }
742
                        }
743
                }
1✔
744

745
                sql.append(")");
1✔
746
                return sql;
1✔
747
        }
748

749
        /**
750
         * SQL_ID文字列の生成
751
         *
752
         * @param metadata エンティティメタ情報
753
         * @param entityType エイティティタイプ
754
         * @return SQL_ID文字列
755
         */
756
        protected String createSqlId(final TableMetadata metadata, final Class<? extends Object> entityType) {
757
                return "mapping @ " + (entityType != null ? entityType.getSimpleName() : metadata.getTableName());
1✔
758
        }
759

760
        /**
761
         * 文字列型かどうかを判定する
762
         *
763
         * @param type JDBC上の型
764
         * @return 文字列型の場合<code>true</code>
765
         */
766
        protected boolean isStringType(final int type) {
767
                return JDBCType.CHAR.getVendorTypeNumber().equals(type) || JDBCType.NCHAR.getVendorTypeNumber().equals(type)
1✔
768
                                || JDBCType.VARCHAR.getVendorTypeNumber().equals(type)
1✔
769
                                || JDBCType.NVARCHAR.getVendorTypeNumber().equals(type)
1✔
770
                                || JDBCType.LONGNVARCHAR.getVendorTypeNumber().equals(type);
1✔
771
        }
772

773
        /**
774
         * IFコメントでSQLをラップする
775
         *
776
         * @param original SQL
777
         * @param addParts IFコメントの中に含まれるSQLパーツ
778
         * @param col カラム情報
779
         * @return SQL
780
         */
781
        protected StringBuilder wrapIfComment(final StringBuilder original, final StringBuilder addParts,
782
                        final TableMetadata.Column col) {
783
                var camelColName = col.getCamelColumnName();
1✔
784
                // フィールドがセットされていない場合はカラム自体を削る
785
                if (isStringType(col.getDataType()) && emptyStringEqualsNull && !col.isNullable()) {
1✔
786
                        original.append("/*IF SF.isNotEmpty(").append(camelColName).append(") */")
1✔
787
                                        .append(System.lineSeparator());
1✔
788
                } else {
789
                        original.append("/*IF ").append(camelColName).append(" != null */").append(System.lineSeparator());
1✔
790
                }
791
                original.append(addParts);
1✔
792
                original.append("/*END*/").append(System.lineSeparator());
1✔
793
                return original;
1✔
794
        }
795

796
        /**
797
         * entityのフィールドの値をExecutionContextにバインドする.
798
         *
799
         * @param context ExecutionContext
800
         * @param entity フィールドをバインドするEntityオブジェクト
801
         * @param kind SQL種別
802
         * @param getParamName パラメータ名取得関数
803
         */
804
        protected void setFields(final ExecutionContext context, final Object entity, final SqlKind kind,
805
                        final Function<MappingColumn, String> getParamName) {
806
                var generatedKeyColumns = new ArrayList<String>();
1✔
807
                if (context.getGeneratedKeyColumns() != null) {
1✔
808
                        for (var keyColumn : context.getGeneratedKeyColumns()) {
1✔
809
                                generatedKeyColumns.add(CaseFormat.CAMEL_CASE.convert(keyColumn));
1✔
810
                        }
811
                }
812
                for (var column : MappingUtils.getMappingColumns(context.getSchema(), entity.getClass(), kind)) {
1✔
813
                        if (SqlKind.INSERT.equals(kind) && generatedKeyColumns.contains(column.getCamelName())) {
1✔
814
                                continue;
1✔
815
                        }
816

817
                        context.param(getParamName.apply(column), column.getValue(entity));
1✔
818
                }
819
        }
1✔
820

821
        /**
822
         * 一括実行時に設定するバインドパラメータ名を取得する.
823
         *
824
         * @param base 生成の元となるパラメータ名
825
         * @param entityIndex エンティティのインデックス
826
         * @return 生成したバンドパラメータ名
827
         */
828
        protected String buildBulkParamName(final String base, final int entityIndex) {
829
                return base + "$" + entityIndex;
1✔
830
        }
831

832
        /**
833
         * {@inheritDoc}
834
         *
835
         * @see jp.co.future.uroborosql.mapping.EntityHandler#initialize()
836
         */
837
        @Override
838
        public void initialize() {
839
                this.propertyMapperManager = new PropertyMapperManager(getSqlConfig().getClock());
1✔
840
        }
1✔
841

842
        /**
843
         * {@inheritDoc}
844
         *
845
         * @see jp.co.future.uroborosql.config.SqlConfigAware#setSqlConfig(jp.co.future.uroborosql.config.SqlConfig)
846
         */
847
        @Override
848
        public void setSqlConfig(final SqlConfig sqlConfig) {
849
                this.sqlConfig = sqlConfig;
1✔
850
        }
1✔
851

852
        /**
853
         * {@inheritDoc}
854
         *
855
         * @see jp.co.future.uroborosql.config.SqlConfigAware#getSqlConfig()
856
         */
857
        @Override
858
        public SqlConfig getSqlConfig() {
859
                return this.sqlConfig;
1✔
860
        }
861

862
        /**
863
         * TableMetadataのLRUキャッシュをクリアします.
864
         */
865
        public static void clearCache() {
866
                CACHE.clear();
1✔
867
        }
1✔
868

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