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

future-architect / uroborosql / #715

pending completion
#715

push

web-flow
merge v0.x changes to master (#310)

* v0.x to master merge

* fix nanoseconds diff (java8 to java11)

* eclipse cleanup and  optimize import

* eclipse format

* optimize Imports

* remove unused annotation

* library version up

* migrate v0.x to master
- update java version 8 to 11
- update junit4 to junit5
- library version update

* fix test failed

* remove unused annotation

* fixed bug

* use var

* fix java8 coding style

1154 of 1154 new or added lines in 77 files covered. (100.0%)

7721 of 8533 relevant lines covered (90.48%)

0.9 hits per line

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

96.87
/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.Arrays;
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.StringUtils;
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
         * {@inheritDoc}
58
         *
59
         * @see jp.co.future.uroborosql.mapping.EntityHandler#isEmptyStringEqualsNull()
60
         */
61
        @Override
62
        public boolean isEmptyStringEqualsNull() {
63
                return emptyStringEqualsNull;
×
64
        }
65

66
        /**
67
         * {@inheritDoc}
68
         *
69
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setEmptyStringEqualsNull(boolean)
70
         */
71
        @Override
72
        public EntityHandler<Object> setEmptyStringEqualsNull(final boolean emptyStringEqualsNull) {
73
                this.emptyStringEqualsNull = emptyStringEqualsNull;
1✔
74
                return this;
1✔
75
        }
76

77
        /**
78
         * {@inheritDoc}
79
         *
80
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createSelectContext(SqlAgent, TableMetadata, Class, boolean)
81
         */
82
        @Override
83
        public ExecutionContext createSelectContext(final SqlAgent agent, final TableMetadata metadata,
84
                        final Class<? extends Object> entityType, final boolean addCondition) {
85
                return agent.contextWith(buildSelectSQL(metadata, entityType, agent.getSqlConfig(), addCondition))
1✔
86
                                .setSqlId(createSqlId(metadata, entityType))
1✔
87
                                .setSchema(metadata.getSchema())
1✔
88
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
89
        }
90

91
        /**
92
         * {@inheritDoc}
93
         *
94
         * @see jp.co.future.uroborosql.mapping.EntityHandler#doSelect(SqlAgent, ExecutionContext, Class)
95
         */
96
        @Override
97
        public <E> Stream<E> doSelect(final SqlAgent agent, final ExecutionContext context,
98
                        final Class<? extends E> entityType)
99
                        throws SQLException {
100
                return agent.query(context,
1✔
101
                                new EntityResultSetConverter<>(context.getSchema(), entityType, propertyMapperManager));
1✔
102
        }
103

104
        /**
105
         * {@inheritDoc}
106
         *
107
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createInsertContext(SqlAgent, TableMetadata, Class)
108
         */
109
        @Override
110
        public ExecutionContext createInsertContext(final SqlAgent agent, final TableMetadata metadata,
111
                        final Class<? extends Object> entityType) {
112
                return agent.contextWith(buildInsertSQL(metadata, entityType, agent.getSqlConfig()))
1✔
113
                                .setSqlId(createSqlId(metadata, entityType))
1✔
114
                                .setSchema(metadata.getSchema())
1✔
115
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
116
        }
117

118
        /**
119
         * {@inheritDoc}
120
         *
121
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createUpdateContext(SqlAgent, TableMetadata, Class, boolean)
122
         */
123
        @Override
124
        public ExecutionContext createUpdateContext(final SqlAgent agent, final TableMetadata metadata,
125
                        final Class<? extends Object> entityType, final boolean addCondition) {
126
                return agent.contextWith(buildUpdateSQL(metadata, entityType, agent.getSqlConfig(), addCondition, true))
1✔
127
                                .setSqlId(createSqlId(metadata, entityType))
1✔
128
                                .setSchema(metadata.getSchema())
1✔
129
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
130
        }
131

132
        /**
133
         * {@inheritDoc}
134
         *
135
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createDeleteContext(SqlAgent, TableMetadata, Class, boolean)
136
         */
137
        @Override
138
        public ExecutionContext createDeleteContext(final SqlAgent agent, final TableMetadata metadata,
139
                        final Class<? extends Object> entityType, final boolean addCondition) {
140
                return agent.contextWith(buildDeleteSQL(metadata, entityType, agent.getSqlConfig(), addCondition))
1✔
141
                                .setSqlId(createSqlId(metadata, entityType))
1✔
142
                                .setSchema(metadata.getSchema())
1✔
143
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
144
        }
145

146
        /**
147
         * {@inheritDoc}
148
         *
149
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createBatchInsertContext(SqlAgent, TableMetadata, Class)
150
         */
151
        @Override
152
        public ExecutionContext createBatchInsertContext(final SqlAgent agent, final TableMetadata metadata,
153
                        final Class<? extends Object> entityType) {
154
                return agent.contextWith(buildInsertSQL(metadata, entityType, agent.getSqlConfig(), false))
1✔
155
                                .setSqlId(createSqlId(metadata, entityType))
1✔
156
                                .setSchema(metadata.getSchema())
1✔
157
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
158
        }
159

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

174
        /**
175
         * {@inheritDoc}
176
         *
177
         * @see jp.co.future.uroborosql.mapping.EntityHandler#createBatchUpdateContext(SqlAgent, TableMetadata, Class)
178
         */
179
        @Override
180
        public ExecutionContext createBatchUpdateContext(final SqlAgent agent, final TableMetadata metadata,
181
                        final Class<? extends Object> entityType) {
182
                return agent.contextWith(buildUpdateSQL(metadata, entityType, agent.getSqlConfig(), true, false))
1✔
183
                                .setSqlId(createSqlId(metadata, entityType))
1✔
184
                                .setSchema(metadata.getSchema())
1✔
185
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
186
        }
187

188
        /**
189
         * {@inheritDoc}
190
         *
191
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setupSqlBulkInsertContext(SqlAgent, ExecutionContext, TableMetadata, Class, int)
192
         */
193
        @Override
194
        public ExecutionContext setupSqlBulkInsertContext(final SqlAgent agent, final ExecutionContext context,
195
                        final TableMetadata metadata, final Class<? extends Object> entityType, final int numberOfRecords) {
196
                return context.setSql(buildBulkInsertSQL(metadata, entityType, agent.getSqlConfig(), numberOfRecords))
1✔
197
                                .setSchema(metadata.getSchema())
1✔
198
                                .setSqlName(entityType != null ? entityType.getCanonicalName() : null);
1✔
199
        }
200

201
        /**
202
         * {@inheritDoc}
203
         *
204
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setInsertParams(ExecutionContext, Object)
205
         */
206
        @Override
207
        public void setInsertParams(final ExecutionContext context, final Object entity) {
208
                setFields(context, entity, SqlKind.INSERT, MappingColumn::getCamelName);
1✔
209
        }
1✔
210

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

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

231
        /**
232
         * {@inheritDoc}
233
         *
234
         * @see jp.co.future.uroborosql.mapping.EntityHandler#setBulkInsertParams(ExecutionContext, Object, int)
235
         */
236
        @Override
237
        public void setBulkInsertParams(final ExecutionContext context, final Object entity, final int entityIndex) {
238
                setFields(context, entity, SqlKind.INSERT, col -> buildBulkParamName(col.getCamelName(), entityIndex));
1✔
239
        }
1✔
240

241
        /**
242
         * {@inheritDoc}
243
         *
244
         * @see jp.co.future.uroborosql.mapping.EntityHandler#getEntityType()
245
         */
246
        @Override
247
        public Class<Object> getEntityType() {
248
                return Object.class;
1✔
249
        }
250

251
        @Override
252
        public TableMetadata getMetadata(final ConnectionManager connectionManager, final Class<?> entityType)
253
                        throws SQLException {
254
                var cacheKey = getCacheKey(connectionManager, entityType);
1✔
255
                return CACHE.get(cacheKey, key -> {
1✔
256
                        try {
257
                                return createMetadata(connectionManager, entityType);
1✔
258
                        } catch (SQLException e) {
×
259
                                throw new UroborosqlSQLException(e);
×
260
                        }
261
                });
262
        }
263

264
        private String getCacheKey(final ConnectionManager connectionManager, final Class<?> entityType) {
265
                try {
266
                        var table = MappingUtils.getTable(entityType);
1✔
267
                        var schema = StringUtils.isNotEmpty(table.getSchema()) ? table.getSchema()
1✔
268
                                        : Objects.toString(connectionManager.getConnection().getSchema(), "");
1✔
269
                        return String.format("%s.%s", schema.toUpperCase(), entityType.getName());
1✔
270
                } catch (SQLException ex) {
×
271
                        return entityType.getName();
×
272
                }
273
        }
274

275
        /**
276
         * {@inheritDoc}
277
         *
278
         * @see jp.co.future.uroborosql.mapping.EntityHandler#addPropertyMapper(jp.co.future.uroborosql.mapping.mapper.PropertyMapper)
279
         */
280
        @Override
281
        public EntityHandler<Object> addPropertyMapper(final PropertyMapper<?> propertyMapper) {
282
                propertyMapperManager.addMapper(propertyMapper);
1✔
283
                return this;
1✔
284
        }
285

286
        /**
287
         * {@inheritDoc}
288
         *
289
         * @see jp.co.future.uroborosql.mapping.EntityHandler#removePropertyMapper(jp.co.future.uroborosql.mapping.mapper.PropertyMapper)
290
         */
291
        @Override
292
        public EntityHandler<Object> removePropertyMapper(final PropertyMapper<?> propertyMapper) {
293
                propertyMapperManager.removeMapper(propertyMapper);
1✔
294
                return this;
1✔
295
        }
296

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

312
        /**
313
         * エンティティ型からテーブル情報の取得
314
         *
315
         * @param type エンティティ型
316
         * @return テーブル情報
317
         */
318
        protected Table getTable(final Class<? extends Object> type) {
319
                return MappingUtils.getTable(type);
1✔
320
        }
321

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

335
                var sql = new StringBuilder(
1✔
336
                                buildSelectClause(metadata, type, sqlConfig.getSqlAgentProvider().getSqlIdKeyName()));
1✔
337

338
                if (addCondition) {
1✔
339
                        sql.append("/*BEGIN*/").append(System.lineSeparator());
1✔
340
                        sql.append("WHERE").append(System.lineSeparator());
1✔
341

342
                        for (var col : columns) {
1✔
343
                                var camelColName = col.getCamelColumnName();
1✔
344
                                var parts = new StringBuilder().append("\t").append("AND ")
1✔
345
                                                .append(col.getColumnIdentifier())
1✔
346
                                                .append(" = ").append("/*").append(camelColName).append("*/''").append(System.lineSeparator());
1✔
347
                                wrapIfComment(sql, parts, col);
1✔
348
                        }
1✔
349
                        sql.append("/*END*/").append(System.lineSeparator());
1✔
350

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

369
                return sql.toString();
1✔
370
        }
371

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

384
                var sql = new StringBuilder("SELECT ").append("/* ").append(sqlIdKeyName).append(" */")
1✔
385
                                .append(System.lineSeparator());
1✔
386

387
                var firstFlag = true;
1✔
388
                for (var col : columns) {
1✔
389
                        sql.append("\t");
1✔
390
                        if (firstFlag) {
1✔
391
                                sql.append("  ");
1✔
392
                                firstFlag = false;
1✔
393
                        } else {
394
                                sql.append(", ");
1✔
395
                        }
396
                        sql.append(col.getColumnIdentifier()).append("\tAS\t").append(col.getColumnIdentifier());
1✔
397
                        if (StringUtils.isNotEmpty(col.getRemarks())) {
1✔
398
                                sql.append("\t").append("-- ").append(col.getRemarks());
1✔
399
                        }
400
                        sql.append(System.lineSeparator());
1✔
401
                }
1✔
402
                sql.append("FROM ").append(metadata.getTableIdentifier()).append(System.lineSeparator());
1✔
403

404
                return sql.toString();
1✔
405
        }
406

407
        /**
408
         * INSERT SQL生成
409
         *
410
         * @param metadata エンティティメタ情報
411
         * @param type エイティティタイプ
412
         * @param sqlConfig SQLコンフィグ
413
         * @return INSERT SQL
414
         */
415
        protected String buildInsertSQL(final TableMetadata metadata, final Class<? extends Object> type,
416
                        final SqlConfig sqlConfig) {
417
                return buildInsertSQL(metadata, type, sqlConfig, true);
1✔
418
        }
419

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

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

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

464
        /**
465
         * UPDATE SQL生成
466
         *
467
         * @param metadata エンティティメタ情報
468
         * @param type エイティティタイプ
469
         * @param sqlConfig SQLコンフィグ
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 SqlConfig sqlConfig, final boolean addCondition, final boolean ignoreWhenEmpty) {
476
                var sql = new StringBuilder("UPDATE ").append("/* ")
1✔
477
                                .append(sqlConfig.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 (StringUtils.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
                                        : Arrays.asList(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 sqlConfig SQLコンフィグ
597
         * @param addCondition 条件を追加するかどうか。追加する場合<code>true</code>
598
         * @return DELETE SQL
599
         */
600
        protected String buildDeleteSQL(final TableMetadata metadata, final Class<? extends Object> type,
601
                        final SqlConfig sqlConfig, final boolean addCondition) {
602
                var sql = new StringBuilder("DELETE ").append("/* ")
1✔
603
                                .append(sqlConfig.getSqlAgentProvider().getSqlIdKeyName()).append(" */")
1✔
604
                                .append(" FROM ").append(metadata.getTableIdentifier()).append("").append(System.lineSeparator());
1✔
605

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

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

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

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

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

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

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

689
                sql.append(")");
1✔
690
                return sql;
1✔
691
        }
692

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

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

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

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

748
                sql.append(")");
1✔
749
                return sql;
1✔
750
        }
751

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

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

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

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

820
                        context.param(getParamName.apply(column), column.getValue(entity));
1✔
821
                }
822
        }
1✔
823

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

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

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

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

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

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