• 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

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

9
import java.lang.reflect.InvocationTargetException;
10
import java.lang.reflect.Modifier;
11
import java.sql.ResultSet;
12
import java.sql.SQLException;
13
import java.sql.Timestamp;
14
import java.sql.Types;
15
import java.time.Clock;
16
import java.time.DayOfWeek;
17
import java.time.Instant;
18
import java.time.LocalDate;
19
import java.time.LocalDateTime;
20
import java.time.LocalTime;
21
import java.time.Month;
22
import java.time.MonthDay;
23
import java.time.OffsetDateTime;
24
import java.time.OffsetTime;
25
import java.time.Year;
26
import java.time.YearMonth;
27
import java.time.ZonedDateTime;
28
import java.time.chrono.ChronoLocalDate;
29
import java.time.chrono.Era;
30
import java.time.format.DateTimeFormatter;
31
import java.time.format.DateTimeFormatterBuilder;
32
import java.time.format.DateTimeParseException;
33
import java.time.temporal.ChronoField;
34
import java.time.temporal.TemporalAccessor;
35
import java.util.Optional;
36

37
import jp.co.future.uroborosql.exception.UroborosqlRuntimeException;
38
import jp.co.future.uroborosql.mapping.JavaType;
39
import jp.co.future.uroborosql.utils.ObjectUtils;
40

41
/**
42
 * Date and Time API用{@link PropertyMapper}
43
 *
44
 * @author ota
45
 */
46
public class DateTimeApiPropertyMapper implements PropertyMapper<TemporalAccessor> {
47
        /**
48
         * yyyyMMddHHmmss文字列からLocalDateTimeに変換するためのフォーマッター.
49
         */
50
        private static final DateTimeFormatter FORMATTER_SHORT_DATE_TIME = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
1✔
51

52
        /**
53
         * yyyyMMddHHmmssSSS文字列からLocalDateTimeに変換するためのフォーマッター.<br>
54
         */
55
        private static final DateTimeFormatter FORMATTER_SHORT_DATE_TIME_WITH_MILLS = DateTimeFormatter
1✔
56
                        .ofPattern("yyyyMMddHHmmssSSS");
1✔
57

58
        /**
59
         * yyyyMMddHHmmssSSSSSS文字列からLocalDateTimeに変換するためのフォーマッター.<br>
60
         */
61
        private static final DateTimeFormatter FORMATTER_SHORT_DATE_TIME_WITH_MICROS = DateTimeFormatter
1✔
62
                        .ofPattern("yyyyMMddHHmmssSSSSSS");
1✔
63

64
        /**
65
         * yyyyMMddHHmmssSSSSSSSSS文字列からLocalDateTimeに変換するためのフォーマッター.<br>
66
         */
67
        private static final DateTimeFormatter FORMATTER_SHORT_DATE_TIME_WITH_NANOS = DateTimeFormatter
1✔
68
                        .ofPattern("yyyyMMddHHmmssSSSSSSSSS");
1✔
69

70
        /**
71
         * HHmm文字列からLocalTimeに変換するためのフォーマッター.
72
         */
73
        private static final DateTimeFormatter FORMATTER_SHORT_TIME_WITHOUT_SEC = DateTimeFormatter.ofPattern("HHmm");
1✔
74

75
        /**
76
         * HHmmss, HHmmssS ~ HHmmssSSSSSSSSSの文字列からLocalTimeに変換するためのフォーマッター.
77
         */
78
        private static final DateTimeFormatter FORMATTER_SHORT_TIME_WITH_NANOS = new DateTimeFormatterBuilder()
1✔
79
                        .appendPattern("HHmmss")
1✔
80
                        .optionalStart()
1✔
81
                        .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, false)
1✔
82
                        .optionalEnd()
1✔
83
                        .toFormatter();
1✔
84

85
        /**
86
         * 日時の変換に使用するClock
87
         */
88
        private final Clock clock;
89

90
        /**
91
         * コンストラクタ.
92
         *
93
         * @param clock 日時の変換に使用するClock
94
         */
95
        public DateTimeApiPropertyMapper(final Clock clock) {
1✔
96
                this.clock = clock;
1✔
97
        }
1✔
98

99
        /**
100
         * {@inheritDoc}
101
         *
102
         * @see jp.co.future.uroborosql.mapping.mapper.PropertyMapper#canAccept(java.lang.Class)
103
         */
104
        @Override
105
        public boolean canAccept(final Class<?> type) {
106
                return LocalDateTime.class.equals(type)
1✔
107
                                || OffsetDateTime.class.equals(type)
1✔
108
                                || ZonedDateTime.class.equals(type)
1✔
109
                                || LocalDate.class.equals(type)
1✔
110
                                || LocalTime.class.equals(type)
1✔
111
                                || OffsetTime.class.equals(type)
1✔
112
                                || Year.class.equals(type)
1✔
113
                                || YearMonth.class.equals(type)
1✔
114
                                || MonthDay.class.equals(type)
1✔
115
                                || Month.class.equals(type)
1✔
116
                                || DayOfWeek.class.equals(type)
1✔
117
                                || checkEra(type)
1✔
118
                                || checkChronoLocalDate(type);
1✔
119
        }
120

121
        private boolean checkChronoLocalDate(final Class<?> type) {
122
                if (!ChronoLocalDate.class.isAssignableFrom(type)) {
1✔
123
                        return false;
1✔
124
                }
125
                try {
126
                        var method = type.getMethod("of", int.class, int.class, int.class);
1✔
127
                        return Modifier.isStatic(method.getModifiers())
1✔
128
                                        && Modifier.isPublic(method.getModifiers())
1✔
129
                                        && method.getReturnType().equals(type);
1✔
130
                } catch (NoSuchMethodException | SecurityException ex) {
1✔
131
                        return false;
1✔
132
                }
133
        }
134

135
        private boolean checkEra(final Class<?> type) {
136
                if (!Era.class.isAssignableFrom(type)) {
1✔
137
                        return false;
1✔
138
                }
139
                try {
140
                        var method = type.getMethod("of", int.class);
1✔
141
                        return Modifier.isStatic(method.getModifiers())
1✔
142
                                        && Modifier.isPublic(method.getModifiers())
1✔
143
                                        && method.getReturnType().equals(type);
1✔
144
                } catch (NoSuchMethodException | SecurityException ex) {
1✔
145
                        return false;
1✔
146
                }
147
        }
148

149
        /**
150
         * {@inheritDoc}
151
         *
152
         * @see jp.co.future.uroborosql.mapping.mapper.PropertyMapper#getValue(jp.co.future.uroborosql.mapping.JavaType, java.sql.ResultSet, int, jp.co.future.uroborosql.mapping.mapper.PropertyMapperManager)
153
         */
154
        @Override
155
        public TemporalAccessor getValue(final JavaType type, final ResultSet rs, final int columnIndex,
156
                        final PropertyMapperManager mapperManager) throws SQLException {
157
                Class<?> rawType = type.getRawType();
1✔
158
                if (LocalDateTime.class.equals(rawType)) {
1✔
159
                        return getLocalDateTime(rs, columnIndex)
1✔
160
                                        .orElse(null);
1✔
161
                }
162
                if (OffsetDateTime.class.equals(rawType)) {
1✔
163
                        return getLocalDateTime(rs, columnIndex)
1✔
164
                                        .map(d -> OffsetDateTime.of(d, OffsetDateTime.now(clock).getOffset()))
1✔
165
                                        .orElse(null);
1✔
166
                }
167
                if (ZonedDateTime.class.equals(rawType)) {
1✔
168
                        return getLocalDateTime(rs, columnIndex)
1✔
169
                                        .map(d -> ZonedDateTime.of(d, clock.getZone()))
1✔
170
                                        .orElse(null);
1✔
171
                }
172
                if (LocalDate.class.equals(rawType)) {
1✔
173
                        return getLocalDate(rs, columnIndex)
1✔
174
                                        .orElse(null);
1✔
175
                }
176
                if (LocalTime.class.equals(rawType)) {
1✔
177
                        return getLocalTime(rs, columnIndex)
1✔
178
                                        .orElse(null);
1✔
179
                }
180
                if (OffsetTime.class.equals(rawType)) {
1✔
181
                        return getLocalTime(rs, columnIndex)
1✔
182
                                        .map(time -> OffsetTime.of(time, clock.getZone().getRules().getOffset(Instant.EPOCH)))
1✔
183
                                        .orElse(null);
1✔
184
                }
185

186
                if (Year.class.equals(rawType)) {
1✔
187
                        var value = getInt(rs, columnIndex);
1✔
188
                        return rs.wasNull() ? null : Year.of(value);
1✔
189
                }
190
                if (YearMonth.class.equals(rawType)) {
1✔
191
                        var value = getInt(rs, columnIndex);
1✔
192
                        if (rs.wasNull()) {
1✔
193
                                return null;
1✔
194
                        }
195
                        return YearMonth.of(value / 100, value % 100);
1✔
196
                }
197
                if (MonthDay.class.equals(rawType)) {
1✔
198
                        var value = getInt(rs, columnIndex);
1✔
199
                        if (rs.wasNull()) {
1✔
200
                                return null;
1✔
201
                        }
202
                        return MonthDay.of(value / 100, value % 100);
1✔
203
                }
204
                if (Month.class.equals(rawType)) {
1✔
205
                        var value = getInt(rs, columnIndex);
1✔
206
                        return rs.wasNull() ? null : Month.of(value);
1✔
207
                }
208
                if (DayOfWeek.class.equals(rawType)) {
1✔
209
                        var value = getInt(rs, columnIndex);
1✔
210
                        return rs.wasNull() ? null : DayOfWeek.of(value);
1✔
211
                }
212
                if (Era.class.isAssignableFrom(rawType)) {
1✔
213
                        var value = getInt(rs, columnIndex);
1✔
214
                        try {
215
                                return rs.wasNull() ? null : (Era) rawType.getMethod("of", int.class).invoke(null, value);
1✔
NEW
216
                        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException
×
217
                                        | SecurityException ex) {
NEW
218
                                throw new UroborosqlRuntimeException(ex);
×
219
                        }
220
                }
221

222
                // JapaneseDate等のChronoLocalDateの変換
223
                if (ChronoLocalDate.class.isAssignableFrom(rawType)) {
1✔
224
                        var localDate = getLocalDate(rs, columnIndex).orElse(null);
1✔
225
                        if (localDate == null) {
1✔
226
                                return null;
1✔
227
                        }
228

229
                        try {
230
                                return (ChronoLocalDate) rawType.getMethod("of", int.class, int.class, int.class).invoke(null,
1✔
231
                                                localDate.getYear(),
1✔
232
                                                localDate.getMonthValue(),
1✔
233
                                                localDate.getDayOfMonth());
1✔
234
                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
×
235
                                        | NoSuchMethodException | SecurityException ex) {
NEW
236
                                throw new UroborosqlRuntimeException(ex);
×
237
                        }
238
                }
239
                throw new UroborosqlRuntimeException();
×
240
        }
241

242
        /**
243
         * ResultSetの指定したカラムの値からintを取得する.<br>
244
         *
245
         * カラムの型が文字列の場合はintへの変換を試みる.
246
         *
247
         * @param rs 検索結果
248
         * @param columnIndex カラムインデックス
249
         * @return 変換したint. カラムの値がnullまたは空文字の場合は0を返却
250
         * @throws SQLException
251
         */
252
        private int getInt(final ResultSet rs, final int columnIndex) throws SQLException {
253
                var columnType = rs.getMetaData().getColumnType(columnIndex);
1✔
254
                if (isStringType(columnType)) {
1✔
255
                        var str = rs.getString(columnIndex);
1✔
256
                        if (ObjectUtils.isEmpty(str)) {
1✔
257
                                return 0;
1✔
258
                        } else if (ObjectUtils.isNumeric(str)) {
1✔
259
                                return Integer.parseInt(str);
1✔
260
                        } else {
261
                                throw new UroborosqlRuntimeException("Text '" + str + "' could not be parsed to integer.");
×
262
                        }
263
                } else {
264
                        return rs.getInt(columnIndex);
1✔
265
                }
266
        }
267

268
        /**
269
         * ResultSetの指定したカラムの値から{@link LocalDate}オブジェクトを取得する.<br>
270
         *
271
         * カラムの型が文字列の場合は{@link LocalDate}への変換を試みる.
272
         *
273
         * @param rs 検索結果
274
         * @param columnIndex カラムインデックス
275
         * @return 変換した{@link LocalDate}
276
         * @throws SQLException
277
         */
278
        private Optional<LocalDate> getLocalDate(final ResultSet rs, final int columnIndex) throws SQLException {
279
                var columnType = rs.getMetaData().getColumnType(columnIndex);
1✔
280
                if (isStringType(columnType)) {
1✔
281
                        return Optional.ofNullable(rs.getString(columnIndex))
1✔
282
                                        .map(str -> {
1✔
283
                                                try {
284
                                                        if (ObjectUtils.isEmpty(str)) {
1✔
285
                                                                return null;
1✔
286
                                                        } else if (str.length() == 8) { // yyyyMMdd
1✔
287
                                                                return LocalDate.parse(str, DateTimeFormatter.BASIC_ISO_DATE);
1✔
288
                                                        } else { // yyyy-MM-dd
289
                                                                return LocalDate.parse(str, DateTimeFormatter.ISO_LOCAL_DATE);
1✔
290
                                                        }
291
                                                } catch (DateTimeParseException ex) {
×
292
                                                        throw new UroborosqlRuntimeException(ex);
×
293
                                                }
294
                                        });
295
                } else {
296
                        return Optional.ofNullable(rs.getDate(columnIndex))
1✔
297
                                        .map(java.sql.Date::toLocalDate);
1✔
298
                }
299
        }
300

301
        /**
302
         * ResultSetの指定したカラムの値から{@link LocalDateTime}オブジェクトを取得する.<br>
303
         *
304
         * カラムの型が文字列の場合は{@link LocalDateTime}への変換を試みる.
305
         *
306
         * @param rs 検索結果
307
         * @param columnIndex カラムインデックス
308
         * @return 変換した{@link LocalDateTime}
309
         * @throws SQLException
310
         */
311
        private Optional<LocalDateTime> getLocalDateTime(final ResultSet rs, final int columnIndex) throws SQLException {
312
                var columnType = rs.getMetaData().getColumnType(columnIndex);
1✔
313
                if (isStringType(columnType)) {
1✔
314
                        return Optional.ofNullable(rs.getString(columnIndex))
1✔
315
                                        .map(str -> {
1✔
316
                                                try {
317
                                                        if (ObjectUtils.isEmpty(str)) {
1✔
318
                                                                return null;
1✔
319
                                                        } else if (str.contains("-") && str.contains(":")) { // yyyy-MM-ddTHH:mm:ss, yyyy-MM-ddTHH:mm:ss.S ~ yyyy-MM-ddTHH:mm:ss.SSSSSSSSS
1✔
320
                                                                return LocalDateTime.parse(str, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
1✔
321
                                                        } else if (str.length() == 14) { // yyyyMMddHHmmss
1✔
322
                                                                return LocalDateTime.parse(str, FORMATTER_SHORT_DATE_TIME);
1✔
323
                                                        } else if (str.length() == 17) { // yyyyMMddHHmmssSSS
1✔
324
                                                                return LocalDateTime.parse(str, FORMATTER_SHORT_DATE_TIME_WITH_MILLS);
1✔
325
                                                        } else if (str.length() == 20) { // yyyyMMddHHmmssSSSSSS
1✔
326
                                                                return LocalDateTime.parse(str, FORMATTER_SHORT_DATE_TIME_WITH_MICROS);
1✔
327
                                                        } else { // yyyyMMddHHmmssSSSSSSSSS
328
                                                                return LocalDateTime.parse(str, FORMATTER_SHORT_DATE_TIME_WITH_NANOS);
1✔
329
                                                        }
330
                                                } catch (DateTimeParseException ex) {
×
331
                                                        throw new UroborosqlRuntimeException(ex);
×
332
                                                }
333
                                        });
334
                } else {
335
                        return Optional.ofNullable(rs.getTimestamp(columnIndex))
1✔
336
                                        .map(Timestamp::toLocalDateTime);
1✔
337
                }
338
        }
339

340
        /**
341
         * ResultSetの指定したカラムの値から{@link LocalTime}オブジェクトを取得する.<br>
342
         *
343
         * カラムの型が文字列の場合は{@link LocalTime}への変換を試みる.
344
         *
345
         * @param rs 検索結果
346
         * @param columnIndex カラムインデックス
347
         * @return 変換した{@link LocalTime}
348
         * @throws SQLException
349
         */
350
        private Optional<LocalTime> getLocalTime(final ResultSet rs, final int columnIndex) throws SQLException {
351
                var columnType = rs.getMetaData().getColumnType(columnIndex);
1✔
352
                if (isStringType(columnType)) {
1✔
353
                        return Optional.ofNullable(rs.getString(columnIndex))
1✔
354
                                        .map(str -> {
1✔
355
                                                try {
356
                                                        if (ObjectUtils.isEmpty(str)) {
1✔
357
                                                                return null;
1✔
358
                                                        } else if (str.contains(":")) { // HH:mm, HH:mm:ss, HH:mm:ss.S ~ HH:mm:ss.SSSSSSSSS
1✔
359
                                                                return LocalTime.parse(str, DateTimeFormatter.ISO_LOCAL_TIME);
1✔
360
                                                        } else if (str.length() == 4) { // HHmm
1✔
361
                                                                return LocalTime.parse(str, FORMATTER_SHORT_TIME_WITHOUT_SEC);
1✔
362
                                                        } else { // HHmmss, HHmmssS ~ HHmmssSSSSSSSSS
363
                                                                return LocalTime.parse(str, FORMATTER_SHORT_TIME_WITH_NANOS);
1✔
364
                                                        }
365
                                                } catch (DateTimeParseException ex) {
×
366
                                                        throw new UroborosqlRuntimeException(ex);
×
367
                                                }
368
                                        });
369
                } else {
370
                        return Optional.ofNullable(rs.getTime(columnIndex))
1✔
371
                                        .map(time -> Instant.ofEpochMilli(time.getTime()).atZone(clock.getZone()).toLocalTime());
1✔
372
                }
373
        }
374

375
        /**
376
         * 文字列として扱えるカラム型を判定する
377
         *
378
         * @param columnType カラム型
379
         * @return 文字列として扱える場合<code>true</code>
380
         */
381
        private boolean isStringType(final int columnType) {
382
                return columnType == Types.CHAR ||
1✔
383
                                columnType == Types.NCHAR ||
384
                                columnType == Types.VARCHAR ||
385
                                columnType == Types.NVARCHAR ||
386
                                columnType == Types.LONGVARCHAR ||
387
                                columnType == Types.LONGNVARCHAR;
388
        }
389
}
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