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

future-architect / uroborosql / #743

28 Jun 2024 04:17PM UTC coverage: 90.988% (+0.5%) from 90.484%
#743

push

web-flow
add paramIfNotEmpty method (#318)

* 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

* Refactoring TransactionContextManager

* Refactoring SqlAgent

* 途中コミット

* fix testcase

* fix review

* fix javadoc comments

* merge v0.x PR

* cleanup code

* cleanup test code

* change build status badge

* - agent.query, update, proc, batch にそれぞれSupplierを引数にとるメソッドを追加
- SqlQuery.paramとSqlEntityUpdate.setにFunctionを受け取るメソッドを追加

* testcaseの整形

* - SqlFluent と ExtractionCondition の分離
- Arrays.asList -> List.of への変更
- テストの整形

* - v0.x系の不具合対応の追いつき
- ログ出力の整理

* - SqlKindの整理(ENTITY_XXXの追加)
- REPL_LOGの追加
- Deprecatedメソッドの削除(SqlAgent)
- SqlAgent, ExecutionContextでsetterをfluent APIに変更

* DB接続URLの修正

* add and fix testcases.

* add event testcases.

* fix typo

* add paramIfNotEmpty method and StringUtils rename ObjectUtils

* fix review comments.

* remove unused import

1695 of 1958 new or added lines in 97 files covered. (86.57%)

26 existing lines in 10 files now uncovered.

8249 of 9066 relevant lines covered (90.99%)

0.91 hits per line

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

98.29
/src/main/java/jp/co/future/uroborosql/mapping/MappingUtils.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.lang.reflect.Field;
10
import java.lang.reflect.Modifier;
11
import java.util.Arrays;
12
import java.util.LinkedHashMap;
13
import java.util.Map;
14
import java.util.Objects;
15
import java.util.Optional;
16
import java.util.function.Function;
17
import java.util.stream.Collectors;
18
import java.util.stream.Stream;
19

20
import jp.co.future.uroborosql.enums.SqlKind;
21
import jp.co.future.uroborosql.exception.UroborosqlRuntimeException;
22
import jp.co.future.uroborosql.mapping.annotations.Column;
23
import jp.co.future.uroborosql.mapping.annotations.GeneratedValue;
24
import jp.co.future.uroborosql.mapping.annotations.Id;
25
import jp.co.future.uroborosql.mapping.annotations.SequenceGenerator;
26
import jp.co.future.uroborosql.mapping.annotations.Transient;
27
import jp.co.future.uroborosql.mapping.annotations.Version;
28
import jp.co.future.uroborosql.utils.CaseFormat;
29
import jp.co.future.uroborosql.utils.ObjectUtils;
30

31
/**
32
 * マッピング情報ユーティリティ
33
 *
34
 * @author ota
35
 */
36
public final class MappingUtils {
37
        private static final int CACHE_SIZE = Integer.getInteger("uroborosql.entity.cache.size", 30);
1✔
38

39
        private static final ConcurrentLruCache<String, Map<SqlKind, MappingColumn[]>> CACHE = new ConcurrentLruCache<>(
1✔
40
                        CACHE_SIZE);
41

42
        private MappingUtils() {
43
        }
44

45
        private static class TableImpl implements Table {
46
                private final String name;
47
                private final String schema;
48

49
                protected TableImpl(final String name, final String schema) {
1✔
50
                        this.name = name;
1✔
51
                        this.schema = schema;
1✔
52
                }
1✔
53

54
                @Override
55
                public String getName() {
56
                        return this.name;
1✔
57
                }
58

59
                @Override
60
                public String getSchema() {
61
                        return this.schema;
1✔
62
                }
63
        }
64

65
        private static class MappingColumnImpl implements MappingColumn {
66
                private final Field field;
67
                private final JavaType javaType;
68
                private final String name;
69
                private final String camelName;
70
                private final boolean isId;
71
                private final GeneratedValue generatedValue;
72
                private final SequenceGenerator sequenceGenerator;
73
                private final Transient transientAnno;
74
                private final Version versionAnno;
75

76
                MappingColumnImpl(final Field field, final JavaType javaType) {
1✔
77
                        this.field = field;
1✔
78
                        this.javaType = javaType;
1✔
79
                        var column = field.getAnnotation(Column.class);
1✔
80
                        // アクセス可能にする
81
                        field.setAccessible(true);
1✔
82

83
                        if (column != null) {
1✔
84
                                this.name = column.name();
1✔
85
                                this.camelName = CaseFormat.CAMEL_CASE.convert(column.name());
1✔
86
                        } else {
87
                                this.name = CaseFormat.UPPER_SNAKE_CASE.convert(field.getName());
1✔
88
                                this.camelName = field.getName();
1✔
89
                        }
90
                        this.isId = field.getAnnotation(Id.class) != null;
1✔
91
                        this.generatedValue = field.getAnnotation(GeneratedValue.class);
1✔
92
                        this.sequenceGenerator = field.getAnnotation(SequenceGenerator.class);
1✔
93
                        this.transientAnno = field.getAnnotation(Transient.class);
1✔
94
                        this.versionAnno = field.getAnnotation(Version.class);
1✔
95

96
                        if (this.isId && this.generatedValue == null) {
1✔
97
                                throw new UroborosqlRuntimeException("@Id annotation is set in the field [" + field.getName()
1✔
98
                                                + "]. However, @GeneratedValue annotation is not set.");
99
                        }
100
                }
1✔
101

102
                /**
103
                 * {@inheritDoc}
104
                 *
105
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getValue(Object)
106
                 */
107
                @Override
108
                public Object getValue(final Object entity) {
109
                        try {
110
                                return this.field.get(entity);
1✔
NEW
111
                        } catch (IllegalArgumentException | IllegalAccessException ex) {
×
NEW
112
                                throw new UroborosqlRuntimeException(ex);
×
113
                        }
114
                }
115

116
                /**
117
                 * {@inheritDoc}
118
                 *
119
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#setValue(Object, Object)
120
                 */
121
                @Override
122
                public void setValue(final Object entity, final Object value) {
123
                        try {
124
                                this.field.set(entity, value);
1✔
125
                        } catch (IllegalArgumentException | IllegalAccessException ex) {
1✔
126
                                throw new UroborosqlRuntimeException(ex);
1✔
127
                        }
1✔
128
                }
1✔
129

130
                /**
131
                 * {@inheritDoc}
132
                 *
133
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getJavaType()
134
                 */
135
                @Override
136
                public JavaType getJavaType() {
137
                        return this.javaType;
1✔
138
                }
139

140
                /**
141
                 * {@inheritDoc}
142
                 *
143
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getName()
144
                 */
145
                @Override
146
                public String getName() {
147
                        return this.name;
1✔
148
                }
149

150
                /**
151
                 * {@inheritDoc}
152
                 *
153
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getCamelName()
154
                 */
155
                @Override
156
                public String getCamelName() {
157
                        return this.camelName;
1✔
158
                }
159

160
                /**
161
                 * {@inheritDoc}
162
                 *
163
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#isId()
164
                 */
165
                @Override
166
                public boolean isId() {
167
                        return this.isId;
1✔
168
                }
169

170
                /**
171
                 * {@inheritDoc}
172
                 *
173
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getGeneratedValue()
174
                 */
175
                @Override
176
                public GeneratedValue getGeneratedValue() {
177
                        return this.generatedValue;
1✔
178
                }
179

180
                /**
181
                 * {@inheritDoc}
182
                 *
183
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getSequenceGenerator()
184
                 */
185
                @Override
186
                public SequenceGenerator getSequenceGenerator() {
187
                        return this.sequenceGenerator;
1✔
188
                }
189

190
                /**
191
                 * {@inheritDoc}
192
                 *
193
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getTransient()
194
                 */
195
                @Override
196
                public Transient getTransient() {
197
                        return this.transientAnno;
1✔
198
                }
199

200
                /**
201
                 * {@inheritDoc}
202
                 *
203
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#isTransient(SqlKind)
204
                 */
205
                @Override
206
                public boolean isTransient(final SqlKind sqlKind) {
207
                        if (this.transientAnno == null) {
1✔
208
                                return false;
1✔
209
                        }
210

211
                        switch (sqlKind) {
1✔
212
                        case INSERT:
213
                                return this.transientAnno.insert();
1✔
214
                        case UPDATE:
215
                                return this.transientAnno.update();
1✔
216
                        default:
217
                                return this.transientAnno.insert() && this.transientAnno.update();
1✔
218
                        }
219
                }
220

221
                /**
222
                 * {@inheritDoc}
223
                 *
224
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#isVersion()
225
                 */
226
                @Override
227
                public boolean isVersion() {
228
                        return this.versionAnno != null;
1✔
229
                }
230

231
                /**
232
                 * {@inheritDoc}
233
                 *
234
                 * @see jp.co.future.uroborosql.mapping.MappingColumn#getVersion()
235
                 */
236
                @Override
237
                public Version getVersion() {
238
                        return this.versionAnno;
1✔
239
                }
240
        }
241

242
        /**
243
         * エンティティ型からテーブル情報の取得
244
         *
245
         * @param entityType エンティティ型
246
         * @return テーブル情報
247
         */
248
        public static Table getTable(final Class<?> entityType) {
249
                var table = entityType.getAnnotation(jp.co.future.uroborosql.mapping.annotations.Table.class);
1✔
250
                if (table != null) {
1✔
251
                        return new TableImpl(table.name(), table.schema());
1✔
252
                }
253
                var baseName = entityType.getSimpleName();
1✔
254
                if (baseName.endsWith("Entity")) {
1✔
255
                        baseName = baseName.substring(0, baseName.length() - 6);
1✔
256
                }
257
                return new TableImpl(CaseFormat.UPPER_SNAKE_CASE.convert(baseName), "");
1✔
258
        }
259

260
        /**
261
         * カラムマッピング情報取得
262
         *
263
         * @param entityType エンティティ型
264
         * @param camelColumnName 取得するカラムのキャメルケース名
265
         * @return カラムマッピング情報
266
         * @exception UroborosqlRuntimeException 指定したキャメルケースカラム名に該当する{@link MappingColumn}が見つからなかった場合
267
         */
268
        public static MappingColumn getMappingColumn(final Class<?> entityType, final String camelColumnName) {
269
                return getMappingColumn(null, entityType, camelColumnName);
1✔
270
        }
271

272
        /**
273
         * カラムマッピング情報取得
274
         *
275
         * @param schema スキーマ
276
         * @param entityType エンティティ型
277
         * @param camelColumnName 取得するカラムのキャメルケース名
278
         * @return カラムマッピング情報
279
         * @exception UroborosqlRuntimeException 指定したキャメルケースカラム名に該当する{@link MappingColumn}が見つからなかった場合
280
         */
281
        public static MappingColumn getMappingColumn(final String schema, final Class<?> entityType,
282
                        final String camelColumnName) {
283
                return getMappingColumn(schema, entityType, SqlKind.NONE, camelColumnName);
1✔
284
        }
285

286
        /**
287
         * カラムマッピング情報取得
288
         *
289
         * @param entityType エンティティ型
290
         * @param kind SQL種別
291
         * @param camelColumnName 取得するカラムのキャメルケース名
292
         * @return カラムマッピング情報
293
         * @exception UroborosqlRuntimeException 指定したキャメルケースカラム名に該当する{@link MappingColumn}が見つからなかった場合
294
         */
295
        public static MappingColumn getMappingColumn(final Class<?> entityType, final SqlKind kind,
296
                        final String camelColumnName) {
297
                return getMappingColumn(null, entityType, kind, camelColumnName);
1✔
298
        }
299

300
        /**
301
         * カラムマッピング情報取得
302
         *
303
         * @param schema スキーマ
304
         * @param entityType エンティティ型
305
         * @param kind SQL種別
306
         * @param camelColumnName 取得するカラムのキャメルケース名
307
         * @return カラムマッピング情報
308
         * @exception UroborosqlRuntimeException 指定したキャメルケースカラム名に該当する{@link MappingColumn}が見つからなかった場合
309
         */
310
        public static MappingColumn getMappingColumn(final String schema, final Class<?> entityType, final SqlKind kind,
311
                        final String camelColumnName) {
312
                return getMappingColumnMap(schema, entityType, kind).entrySet().stream()
1✔
313
                                .filter(entry -> entry.getKey().equals(camelColumnName))
1✔
314
                                .map(Map.Entry::getValue)
1✔
315
                                .findFirst()
1✔
316
                                .orElseThrow(() -> new UroborosqlRuntimeException("No such column found. col:" + camelColumnName));
1✔
317
        }
318

319
        /**
320
         * カラムマッピング情報取得
321
         *
322
         * @param entityType エンティティ型
323
         * @return カラムマッピング情報
324
         */
325
        public static MappingColumn[] getMappingColumns(final Class<?> entityType) {
326
                return getMappingColumns(null, entityType);
1✔
327
        }
328

329
        /**
330
         * カラムマッピング情報取得
331
         *
332
         * @param schema スキーマ
333
         * @param entityType エンティティ型
334
         * @return カラムマッピング情報
335
         */
336
        public static MappingColumn[] getMappingColumns(final String schema, final Class<?> entityType) {
337
                return getMappingColumns(schema, entityType, SqlKind.NONE);
1✔
338
        }
339

340
        /**
341
         * カラムマッピング情報取得
342
         *
343
         * @param entityType エンティティ型
344
         * @param kind SQL種別
345
         * @return カラムマッピング情報
346
         */
347
        public static MappingColumn[] getMappingColumns(final Class<?> entityType, final SqlKind kind) {
348
                return getMappingColumns(null, entityType, kind);
1✔
349
        }
350

351
        /**
352
         * カラムマッピング情報取得
353
         *
354
         * @param schema スキーマ
355
         * @param entityType エンティティ型
356
         * @param kind SQL種別
357
         * @return カラムマッピング情報
358
         */
359
        public static MappingColumn[] getMappingColumns(final String schema, final Class<?> entityType,
360
                        final SqlKind kind) {
361
                if (entityType == null) {
1✔
362
                        return new MappingColumn[0];
1✔
363
                }
364

365
                var cacheKey = getCacheKey(schema, entityType);
1✔
366

367
                var cols = CACHE.get(cacheKey, key -> {
1✔
368
                        Map<SqlKind, Map<String, MappingColumn>> fieldsMap = Stream.of(SqlKind.NONE, SqlKind.INSERT, SqlKind.UPDATE)
1✔
369
                                        .collect(Collectors.toMap(Function.identity(), e -> new LinkedHashMap<>()));
1✔
370
                        var implementClass = new JavaType.ImplementClass(entityType);
1✔
371
                        walkFields(entityType, implementClass, fieldsMap);
1✔
372
                        return fieldsMap.entrySet().stream()
1✔
373
                                        .collect(Collectors.toConcurrentMap(Map.Entry::getKey,
1✔
374
                                                        e -> e.getValue().values().toArray(new MappingColumn[e.getValue().size()])));
1✔
375
                });
376
                return cols.computeIfAbsent(kind, k -> cols.get(SqlKind.NONE));
1✔
377
        }
378

379
        private static String getCacheKey(final String schema, final Class<?> entityType) {
380
                var table = getTable(entityType);
1✔
381
                var currentSchema = ObjectUtils.isNotEmpty(table.getSchema()) ? table.getSchema()
1✔
382
                                : Objects.toString(schema, "");
1✔
383
                return String.format("%s.%s", currentSchema.toUpperCase(), entityType.getName());
1✔
384
        }
385

386
        /**
387
         * カラム名(小文字)をキーとしたMapにカラムマッピング情報を取得
388
         *
389
         * @param entityType エンティティ型
390
         * @param kind SQL種別
391
         * @return カラムマッピング情報
392
         */
393
        public static Map<String, MappingColumn> getMappingColumnMap(final Class<?> entityType, final SqlKind kind) {
394
                return getMappingColumnMap(null, entityType, kind);
1✔
395
        }
396

397
        /**
398
         * カラム名(小文字)をキーとしたMapにカラムマッピング情報を取得
399
         *
400
         * @param schema スキーマ
401
         * @param entityType エンティティ型
402
         * @param kind SQL種別
403
         * @return カラムマッピング情報
404
         */
405
        public static Map<String, MappingColumn> getMappingColumnMap(final String schema, final Class<?> entityType,
406
                        final SqlKind kind) {
407
                return Arrays.stream(getMappingColumns(schema, entityType, kind))
1✔
408
                                .collect(Collectors.toMap(MappingColumn::getCamelName, Function.identity()));
1✔
409
        }
410

411
        /**
412
         * IDカラムマッピング情報を返す
413
         *
414
         * @param entityType エンティティ型
415
         * @return カラムマッピング情報
416
         */
417
        public static MappingColumn[] getIdMappingColumns(final Class<?> entityType) {
418
                return getIdMappingColumns(null, entityType);
1✔
419
        }
420

421
        /**
422
         * IDカラムマッピング情報を返す
423
         *
424
         * @param schema スキーマ
425
         * @param entityType エンティティ型
426
         * @return カラムマッピング情報
427
         */
428
        public static MappingColumn[] getIdMappingColumns(final String schema, final Class<?> entityType) {
429
                return Arrays.stream(getMappingColumns(schema, entityType))
1✔
430
                                .filter(MappingColumn::isId)
1✔
431
                                .toArray(MappingColumn[]::new);
1✔
432
        }
433

434
        /**
435
         * バージョン情報のカラムマッピング情報を返す
436
         *
437
         * @param entityType エンティティ型
438
         * @return カラムマッピング情報
439
         */
440
        public static Optional<MappingColumn> getVersionMappingColumn(final Class<?> entityType) {
441
                return getVersionMappingColumn(null, entityType);
1✔
442
        }
443

444
        /**
445
         * バージョン情報のカラムマッピング情報を返す
446
         *
447
         * @param schema スキーマ
448
         * @param entityType エンティティ型
449
         * @return カラムマッピング情報
450
         */
451
        public static Optional<MappingColumn> getVersionMappingColumn(final String schema, final Class<?> entityType) {
452
                return Arrays.stream(getMappingColumns(schema, entityType))
1✔
453
                                .filter(MappingColumn::isVersion)
1✔
454
                                .findFirst();
1✔
455
        }
456

457
        /**
458
         * MappingColumnのキャッシュをクリアします.
459
         */
460
        public static void clearCache() {
461
                CACHE.clear();
1✔
462
        }
1✔
463

464
        private static void walkFields(final Class<?> type, final JavaType.ImplementClass implementClass,
465
                        final Map<SqlKind, Map<String, MappingColumn>> fieldsMap) {
466
                if (type.equals(Object.class)) {
1✔
467
                        return;
1✔
468
                }
469
                Class<?> superclass = type.getSuperclass();
1✔
470
                walkFields(superclass, implementClass, fieldsMap);
1✔
471

472
                var noneColumns = fieldsMap.get(SqlKind.NONE);
1✔
473
                var insertColumns = fieldsMap.get(SqlKind.INSERT);
1✔
474
                var updateColumns = fieldsMap.get(SqlKind.UPDATE);
1✔
475

476
                for (var field : type.getDeclaredFields()) {
1✔
477
                        if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
1✔
478
                                continue;
1✔
479
                        }
480
                        var javaType = JavaType.of(implementClass, field);
1✔
481
                        MappingColumn mappingColumn = new MappingColumnImpl(field, javaType);
1✔
482

483
                        var fieldName = field.getName();
1✔
484
                        noneColumns.put(fieldName, mappingColumn);
1✔
485

486
                        if (mappingColumn.isTransient(SqlKind.NONE)) {
1✔
487
                                continue;// 除外
1✔
488
                        }
489
                        if (!mappingColumn.isTransient(SqlKind.INSERT)) {
1✔
490
                                insertColumns.put(fieldName, mappingColumn);
1✔
491
                        }
492
                        if (!mappingColumn.isTransient(SqlKind.UPDATE)) {
1✔
493
                                updateColumns.put(fieldName, mappingColumn);
1✔
494
                        }
495
                }
496
        }
1✔
497
}
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