• 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

82.61
/src/main/java/jp/co/future/uroborosql/context/ExecutionContextProviderImpl.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.context;
8

9
import java.io.IOException;
10
import java.lang.reflect.Modifier;
11
import java.net.JarURLConnection;
12
import java.net.URISyntaxException;
13
import java.net.URL;
14
import java.nio.file.Files;
15
import java.nio.file.Path;
16
import java.nio.file.Paths;
17
import java.sql.ResultSet;
18
import java.time.Clock;
19
import java.util.ArrayList;
20
import java.util.Collection;
21
import java.util.Collections;
22
import java.util.HashMap;
23
import java.util.HashSet;
24
import java.util.List;
25
import java.util.Map;
26
import java.util.Objects;
27
import java.util.Optional;
28
import java.util.Set;
29
import java.util.StringJoiner;
30
import java.util.concurrent.ConcurrentHashMap;
31
import java.util.jar.JarEntry;
32
import java.util.jar.JarFile;
33
import java.util.stream.Collectors;
34

35
import org.slf4j.Logger;
36
import org.slf4j.LoggerFactory;
37

38
import jp.co.future.uroborosql.config.SqlConfig;
39
import jp.co.future.uroborosql.event.AfterInitializeExecutionContextEvent;
40
import jp.co.future.uroborosql.parameter.Parameter;
41
import jp.co.future.uroborosql.parameter.mapper.BindParameterMapper;
42
import jp.co.future.uroborosql.parameter.mapper.BindParameterMapperManager;
43
import jp.co.future.uroborosql.utils.CaseFormat;
44
import jp.co.future.uroborosql.utils.ObjectUtils;
45

46
/**
47
 * ExecutionContextプロバイダ実装
48
 *
49
 * @author H.Sugimoto
50
 */
51
public class ExecutionContextProviderImpl implements ExecutionContextProvider {
1✔
52
        /** ロガー */
53
        private static final Logger LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.log");
1✔
54

55
        /** 設定ロガー */
56
        private static final Logger SETTING_LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.setting");
1✔
57

58
        /** 定数パラメータプレフィックス */
59
        private String constParamPrefix = "CLS_";
1✔
60

61
        /** 定数クラス名(FQDN) */
62
        private List<String> constantClassNames = new ArrayList<>();
1✔
63
        /** Enum定数パッケージ名 */
64
        private List<String> enumConstantPackageNames = new ArrayList<>();
1✔
65

66
        /** 定数パラメータマップ */
67
        private Map<String, Parameter> constParameterMap = null;
1✔
68

69
        /** SQL設定クラス */
70
        private SqlConfig sqlConfig = null;
1✔
71

72
        /** ResultSetTypeの初期値 */
73
        private int defaultResultSetType = ResultSet.TYPE_FORWARD_ONLY;
1✔
74

75
        /** ResultSetConcurrencyの初期値*/
76
        private int defaultResultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
1✔
77

78
        /** パラメータ変換マネージャ */
79
        private BindParameterMapperManager parameterMapperManager = new BindParameterMapperManager(
1✔
80
                        Clock.systemDefaultZone());
1✔
81

82
        /**
83
         * {@inheritDoc}
84
         *
85
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#createExecutionContext()
86
         */
87
        @Override
88
        public ExecutionContext createExecutionContext() {
89
                var executionContext = new ExecutionContextImpl();
1✔
90
                executionContext.setSqlConfig(getSqlConfig());
1✔
91
                executionContext.setConstParameterMap(new ConcurrentHashMap<>(getConstParameterMap()));
1✔
92
                executionContext.setParameterMapperManager(
1✔
93
                                new BindParameterMapperManager(parameterMapperManager, getSqlConfig().getClock()));
1✔
94
                executionContext.setResultSetType(defaultResultSetType);
1✔
95
                executionContext.setResultSetConcurrency(defaultResultSetConcurrency);
1✔
96

97
                // ExecutionContext初期化後イベント発行
98
                if (getSqlConfig().getEventListenerHolder().hasAfterInitializeExecutionContextListener()) {
1✔
99
                        var eventObj = new AfterInitializeExecutionContextEvent(executionContext);
1✔
100
                        getSqlConfig().getEventListenerHolder().getAfterInitializeExecutionContextListeners()
1✔
101
                                        .forEach(listener -> listener.accept(eventObj));
1✔
102
                }
103
                return executionContext;
1✔
104
        }
105

106
        /**
107
         * {@inheritDoc}
108
         *
109
         * @see jp.co.future.uroborosql.config.SqlConfigAware#setSqlConfig(jp.co.future.uroborosql.config.SqlConfig)
110
         */
111
        @Override
112
        public void setSqlConfig(final SqlConfig sqlConfig) {
113
                this.sqlConfig = sqlConfig;
1✔
114
        }
1✔
115

116
        /**
117
         * {@inheritDoc}
118
         *
119
         * @see jp.co.future.uroborosql.config.SqlConfigAware#getSqlConfig()
120
         */
121
        @Override
122
        public SqlConfig getSqlConfig() {
123
                return sqlConfig;
1✔
124
        }
125

126
        /**
127
         * {@inheritDoc}
128
         *
129
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#initialize()
130
         */
131
        @Override
132
        public void initialize() {
133
                parameterMapperManager = new BindParameterMapperManager(parameterMapperManager, getSqlConfig().getClock());
1✔
134

135
                var paramMap = new HashMap<String, Parameter>(buildConstParamMap());
1✔
136
                paramMap.putAll(buildEnumConstParamMap());
1✔
137
                constParameterMap = Collections.unmodifiableMap(paramMap);
1✔
138
        }
1✔
139

140
        /**
141
         * 定数パラメータのMapを生成する
142
         *
143
         * @param paramMap 定数パラメータを保持するMap
144
         * @param targetClass 定数パラメータを生成する定数クラス。クラス内に内部クラスを持つ場合は内部クラスの定数フィールドもパラメータに登録する
145
         */
146
        protected void makeConstParamMap(final Map<String, Parameter> paramMap,
147
                        final Class<?> targetClass) {
148
                try {
149
                        var fieldPrefix = targetClass.isMemberClass() ? CaseFormat.UPPER_SNAKE_CASE
1✔
150
                                        .convert(targetClass.getSimpleName()) + "_" : "";
1✔
151
                        // 指定されたクラス直下の定数フィールドを追加
152
                        var fields = targetClass.getFields();
1✔
153
                        for (var field : fields) {
1✔
154
                                var mod = field.getModifiers();
1✔
155
                                if (Modifier.isFinal(mod) && Modifier.isStatic(mod)) {
1✔
156
                                        var value = field.get(null);
1✔
157
                                        if (parameterMapperManager.canAcceptByStandard(value)) {
1✔
158
                                                var fieldName = getConstParamPrefix() + fieldPrefix + field.getName();
1✔
159
                                                fieldName = fieldName.toUpperCase();
1✔
160
                                                var newValue = new Parameter(fieldName, field.get(null));
1✔
161
                                                var prevValue = paramMap.put(fieldName, newValue);
1✔
162
                                                if (prevValue != null) {
1✔
NEW
163
                                                        if (SETTING_LOG.isWarnEnabled()) {
×
NEW
164
                                                                SETTING_LOG.warn("Duplicate constant name. Constant name:{}, old value:{} destroy.",
×
165
                                                                                fieldName,
NEW
166
                                                                                prevValue.getValue());
×
167
                                                        }
168
                                                }
169
                                                if (SETTING_LOG.isInfoEnabled()) {
1✔
170
                                                        SETTING_LOG.info("Constant [name:{}, value:{}] added to parameter.",
1✔
171
                                                                        fieldName,
172
                                                                        newValue.getValue());
1✔
173
                                                }
174
                                        }
175
                                }
176
                        }
177

178
                        // 内部クラスを持つ場合
179
                        var memberClasses = targetClass.getDeclaredClasses();
1✔
180
                        for (var memberClass : memberClasses) {
1✔
181
                                var mod = memberClass.getModifiers();
1✔
182
                                if (Modifier.isFinal(mod) && Modifier.isPublic(mod)) {
1✔
183
                                        makeConstParamMap(paramMap, memberClass);
1✔
184
                                }
185
                        }
186
                } catch (IllegalArgumentException | IllegalAccessException | SecurityException ex) {
×
187
                        LOG.error(ex.getMessage(), ex);
×
188
                }
1✔
189
        }
1✔
190

191
        /**
192
         * Enum型の定数パラメータのMapを生成する
193
         *
194
         * @param paramMap 定数パラメータを保持するMap
195
         * @param packageName パッケージ名
196
         * @param targetClass 対象Enumクラス
197
         */
198
        protected void makeEnumConstParamMap(final Map<String, Parameter> paramMap,
199
                        final String packageName,
200
                        final Class<? extends Enum<?>> targetClass) {
201

202
                var fieldPrefix = CaseFormat.UPPER_SNAKE_CASE.convert(targetClass.getName().substring(
1✔
203
                                packageName.length() + 1)) + "_";
1✔
204

205
                var enumValues = targetClass.getEnumConstants();
1✔
206

207
                for (var value : enumValues) {
1✔
208
                        var fieldName = (getConstParamPrefix() + fieldPrefix + value.name().toUpperCase()).toUpperCase();
1✔
209
                        var newValue = new Parameter(fieldName, value);
1✔
210
                        var prevValue = paramMap.put(fieldName, newValue);
1✔
211
                        if (prevValue != null) {
1✔
NEW
212
                                if (SETTING_LOG.isWarnEnabled()) {
×
NEW
213
                                        SETTING_LOG.warn("Duplicate Enum name. Enum name:{}, old value:{} destroy.",
×
214
                                                        fieldName,
NEW
215
                                                        prevValue.getValue());
×
216
                                }
217
                        }
218
                        if (SETTING_LOG.isInfoEnabled()) {
1✔
219
                                SETTING_LOG.info("Enum [name:{}, value:{}] added to parameter.",
1✔
220
                                                fieldName,
221
                                                newValue.getValue());
1✔
222
                        }
223
                }
224
        }
1✔
225

226
        /**
227
         * {inheritDoc}
228
         *
229
         * @see ExecutionContextProvider#getConstParamPrefix()
230
         */
231
        @Override
232
        public String getConstParamPrefix() {
233
                return constParamPrefix;
1✔
234
        }
235

236
        /**
237
         * {inheritDoc}
238
         *
239
         * @see ExecutionContextProvider#setConstParamPrefix(String)
240
         */
241
        @Override
242
        public ExecutionContextProvider setConstParamPrefix(final String constParamPrefix) {
243
                this.constParamPrefix = constParamPrefix;
×
244
                return this;
×
245
        }
246

247
        /**
248
         * {inheritDoc}
249
         *
250
         * @see ExecutionContextProvider#getConstantClassNames()
251
         */
252
        @Override
253
        public List<String> getConstantClassNames() {
254
                return constantClassNames;
×
255
        }
256

257
        /**
258
         * {inheritDoc}
259
         *
260
         * @see ExecutionContextProvider#setConstantClassNames(List)
261
         */
262
        @Override
263
        public ExecutionContextProvider setConstantClassNames(final List<String> constantClassNames) {
264
                this.constantClassNames = constantClassNames;
1✔
265
                return this;
1✔
266
        }
267

268
        /**
269
         * {inheritDoc}
270
         *
271
         * @see ExecutionContextProvider#getConstParameterMap()
272
         */
273
        @Override
274
        public Map<String, Parameter> getConstParameterMap() {
275
                return constParameterMap;
1✔
276
        }
277

278
        /**
279
         * {inheritDoc}
280
         *
281
         * @see ExecutionContextProvider#getEnumConstantPackageNames()
282
         */
283
        @Override
284
        public List<String> getEnumConstantPackageNames() {
285
                return enumConstantPackageNames;
×
286
        }
287

288
        /**
289
         * {inheritDoc}
290
         *
291
         * @see ExecutionContextProvider#setEnumConstantPackageNames(List)
292
         */
293
        @Override
294
        public ExecutionContextProvider setEnumConstantPackageNames(final List<String> enumConstantPackageNames) {
295
                this.enumConstantPackageNames = enumConstantPackageNames;
1✔
296
                return this;
1✔
297
        }
298

299
        /**
300
         * {inheritDoc}
301
         *
302
         * @see ExecutionContextProvider#addBindParamMapper(BindParameterMapper)
303
         */
304
        @Override
305
        public ExecutionContextProvider addBindParamMapper(final BindParameterMapper<?> parameterMapper) {
306
                parameterMapperManager.addMapper(parameterMapper);
1✔
307
                return this;
1✔
308
        }
309

310
        /**
311
         * {inheritDoc}
312
         *
313
         * @see ExecutionContextProvider#removeBindParamMapper(BindParameterMapper)
314
         */
315
        @Override
316
        public ExecutionContextProvider removeBindParamMapper(final BindParameterMapper<?> parameterMapper) {
317
                parameterMapperManager.removeMapper(parameterMapper);
×
318
                return this;
×
319
        }
320

321
        /**
322
         * 定数クラスパラメータMap生成
323
         *
324
         * @return 定数クラスパラメータMap
325
         */
326
        private Map<? extends String, ? extends Parameter> buildConstParamMap() {
327
                var paramMap = new HashMap<String, Parameter>();
1✔
328
                for (var className : constantClassNames) {
1✔
329
                        if (ObjectUtils.isNotBlank(className)) {
1✔
330
                                try {
331
                                        var targetClass = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
1✔
332
                                        makeConstParamMap(paramMap, targetClass);
1✔
333
                                } catch (ClassNotFoundException ex) {
×
334
                                        LOG.error(ex.getMessage(), ex);
×
335
                                }
1✔
336
                        }
337
                }
1✔
338
                return paramMap;
1✔
339
        }
340

341
        /**
342
         * Enum定数パラメータMap生成
343
         *
344
         * @return Enum定数パラメータMap
345
         */
346
        private Map<? extends String, ? extends Parameter> buildEnumConstParamMap() {
347
                var paramMap = new HashMap<String, Parameter>();
1✔
348
                for (var packageName : enumConstantPackageNames) {
1✔
349
                        if (ObjectUtils.isNotBlank(packageName)) {
1✔
350
                                for (var targetClass : listUpEnumClasses(packageName)) {
1✔
351
                                        makeEnumConstParamMap(paramMap, packageName, targetClass);
1✔
352
                                }
1✔
353
                        }
354
                }
1✔
355
                return paramMap;
1✔
356
        }
357

358
        /**
359
         * 対象パッケージ以下のクラスを取得
360
         *
361
         * @param packageName ルートパッケージ名
362
         * @return クラスリスト
363
         */
364
        @SuppressWarnings({ "unchecked", "rawtypes" })
365
        private static Set<Class<? extends Enum<?>>> listUpEnumClasses(final String packageName) {
366
                var resourceName = packageName.replace('.', '/');
1✔
367
                var classLoader = Thread.currentThread().getContextClassLoader();
1✔
368
                List<URL> roots;
369
                try {
370
                        roots = Collections.list(classLoader.getResources(resourceName));
1✔
NEW
371
                } catch (IOException ex) {
×
NEW
372
                        LOG.error(ex.getMessage(), ex);
×
UNCOV
373
                        return Set.of();
×
374
                }
1✔
375

376
                var classes = new HashSet<Class<?>>();
1✔
377
                for (var root : roots) {
1✔
378
                        if ("file".equalsIgnoreCase(root.getProtocol())) {
1✔
379
                                try {
380
                                        classes.addAll(findEnumClassesWithFile(packageName, Paths.get(root.toURI())));
1✔
NEW
381
                                } catch (URISyntaxException ex) {
×
NEW
382
                                        LOG.error(ex.getMessage(), ex);
×
383
                                }
1✔
384
                        }
385
                        if ("jar".equalsIgnoreCase(root.getProtocol())) {
1✔
386
                                try (var jarFile = ((JarURLConnection) root.openConnection()).getJarFile()) {
1✔
387
                                        classes.addAll(findEnumClassesWithJar(packageName, jarFile));
1✔
NEW
388
                                } catch (IOException ex) {
×
NEW
389
                                        LOG.error(ex.getMessage(), ex);
×
390
                                }
1✔
391
                        }
392
                }
1✔
393

394
                return (Set) classes;
1✔
395
        }
396

397
        /**
398
         * classファイルから対象パッケージ以下のEnumクラスを取得
399
         *
400
         * @param packageName ルートパッケージ名
401
         * @param dir 対象ディレクトリ
402
         * @return クラスリスト
403
         * @throws ClassNotFoundException エラー
404
         * @throws IOException
405
         */
406
        private static Set<Class<?>> findEnumClassesWithFile(final String packageName, final Path dir) {
407
                var prefix = packageName + ".";
1✔
408
                try (var stream = Files.walk(dir)) {
1✔
409
                        return stream.filter(entry -> entry.getFileName().toString().endsWith(".class"))
1✔
410
                                        .flatMap(file -> {
1✔
411
                                                var joiner = new StringJoiner(".", prefix, "");
1✔
412
                                                dir.relativize(file).forEach(p -> joiner.add(p.toString()));
1✔
413
                                                var className = joiner.toString().replaceAll(".class$", "");
1✔
414
                                                return loadEnum(className).stream();
1✔
415
                                        })
416
                                        .filter(Objects::nonNull)
1✔
417
                                        .collect(Collectors.toSet());
1✔
NEW
418
                } catch (IOException ex) {
×
NEW
419
                        LOG.error(ex.getMessage(), ex);
×
UNCOV
420
                        return Set.of();
×
421
                }
422
        }
423

424
        /**
425
         * jarファイルから対象パッケージ以下のEnumクラスを取得
426
         *
427
         * @param packageName ルートパッケージ名
428
         * @param jarFile jarファイル
429
         * @return クラスリスト
430
         * @throws ClassNotFoundException エラー
431
         * @throws IOException
432
         */
433
        private static Collection<? extends Class<?>> findEnumClassesWithJar(final String packageName,
434
                        final JarFile jarFile) {
435
                var resourceName = packageName.replace('.', '/');
1✔
436
                return Collections.list(jarFile.entries()).stream()
1✔
437
                                .map(JarEntry::getName)
1✔
438
                                .filter(name -> name.startsWith(resourceName) && name.endsWith(".class"))
1✔
439
                                .map(name -> name.replace('/', '.').replaceAll(".class$", ""))
1✔
440
                                .flatMap(className -> loadEnum(className).stream())
1✔
441
                                .filter(Objects::nonNull)
1✔
442
                                .collect(Collectors.toSet());
1✔
443
        }
444

445
        /**
446
         * Enumクラスをロード<br>
447
         * 指定クラスがEnumでない場合はemptyを返す
448
         *
449
         * @param className クラス名
450
         * @return ロードしたEnumクラス
451
         */
452
        private static Optional<Class<?>> loadEnum(final String className) {
453
                try {
454
                        var type = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
1✔
455
                        if (type.isEnum()) {
1✔
456
                                return Optional.of(type);
1✔
457
                        }
NEW
458
                } catch (ClassNotFoundException ex) {
×
NEW
459
                        LOG.error(ex.getMessage(), ex);
×
460
                }
1✔
461
                return Optional.empty();
1✔
462
        }
463

464
        /**
465
         * {@inheritDoc}
466
         *
467
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#setDefaultResultSetType(int)
468
         */
469
        @Override
470
        public ExecutionContextProvider setDefaultResultSetType(final int resultSetType) {
471
                defaultResultSetType = resultSetType;
1✔
472
                return this;
1✔
473
        }
474

475
        /**
476
         * {@inheritDoc}
477
         *
478
         * @see jp.co.future.uroborosql.context.ExecutionContextProvider#setDefaultResultSetConcurrency(int)
479
         */
480
        @Override
481
        public ExecutionContextProvider setDefaultResultSetConcurrency(final int resultSetConcurrency) {
482
                defaultResultSetConcurrency = resultSetConcurrency;
1✔
483
                return this;
1✔
484
        }
485

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