1. Project Clover database Fri Jul 17 2026 06:10:26 UTC
  2. Package guru.mikelue.foxglove.jdbc

File ParameterSetterFactory.java

 

Coverage histogram

../../../../img/srcFileCovDistChart8.png
70% of files have more coverage

Code metrics

12
29
4
1
191
137
14
0.48
7.25
4
3.5

Classes

Class Line # Actions
ParameterSetterFactory 17 29 0% 14 11
0.7555555775.6%
 

Contributing tests

This file is covered by 49 tests. .

Source view

1    package guru.mikelue.foxglove.jdbc;
2   
3    import java.sql.JDBCType;
4    import java.sql.Types;
5    import java.time.*;
6    import java.util.Map;
7    import java.util.function.Function;
8    import java.util.stream.Collectors;
9    import java.util.stream.Stream;
10   
11    import static java.util.Map.entry;
12   
13    /**
14    * In order to support different behavior of JDBC for setting parameter,
15    * this utility class implements different cases between Java types and SQL types.
16    */
 
17    final class ParameterSetterFactory {
18    private static final Map<Class<?>, Function<JDBCType, CustomStatementSetter<?>>> JAVA_TIME_SETTERS =
19    readonlyMap(
20    /*
21    * Converts these types to OffsetDateTime for most of JDBC drivers(by experiment),
22    * which can handle OffsetDateTime for temporal types
23    */
24    entry(
25    Instant.class, jdbcType -> getSetterForGeneralOffsetDateTime(
26    jdbcType,
27    value -> ((Instant) value).atOffset(ZoneOffset.UTC)
28    )
29    ),
30    entry(
31    ZonedDateTime.class, jdbcType -> getSetterForGeneralOffsetDateTime(
32    jdbcType,
33    value -> ((ZonedDateTime) value).toOffsetDateTime()
34    )
35    ),
36    entry(
37    OffsetDateTime.class, jdbcType -> getSetterForGeneralOffsetDateTime(
38    jdbcType,
39    value -> (OffsetDateTime) value
40    )
41    ),
42    // :~)
43   
44    // Vendor-specific rules for OffsetTime:
45    // mysql/posgres needs TIME,
46    // derby/mssql needs TIMESTAMP_WITH_TIMEZONE
47    entry(
48    OffsetTime.class, jdbcType -> {
49  2 switch (jdbcType) {
50  0 case TIME:
51  0 return (stmt, paramIndex, columnMeta, value) ->
52    stmt.setTime(paramIndex, java.sql.Time.valueOf(((OffsetTime) value).toLocalTime()));
53  2 case TIME_WITH_TIMEZONE:
54  2 return (stmt, paramIndex, columnMeta, value) ->
55    stmt.setObject(paramIndex, ((OffsetTime) value), jdbcType.getVendorTypeNumber());
56  0 default:
57  0 return (stmt, paramIndex, columnMeta, value) ->
58    stmt.setTime(paramIndex, java.sql.Time.valueOf(((OffsetTime) value).toLocalTime()));
59    }
60    }
61    )
62    );
63   
64    private static final Map<Class<?>, JDBCType> TYPE_TO_JDBC_TYPES = readonlyMap(
65    /*
66    * For legacy temporal types of Java
67    */
68    entry(java.sql.Time.class, JDBCType.TIME),
69    entry(java.sql.Date.class, JDBCType.DATE),
70    entry(java.sql.Timestamp.class, JDBCType.TIMESTAMP),
71    entry(java.util.Date.class, JDBCType.TIMESTAMP),
72    // :~)
73   
74    entry(java.time.LocalTime.class, JDBCType.TIME),
75   
76    // LocalDate
77    // Not supported: derby
78    entry(java.time.LocalDate.class, JDBCType.DATE),
79   
80    entry(java.time.LocalDateTime.class, JDBCType.TIMESTAMP),
81   
82    /*
83    * For number types
84    */
85    entry(Byte.class, JDBCType.TINYINT),
86    entry(byte.class, JDBCType.TINYINT),
87    entry(Short.class, JDBCType.SMALLINT),
88    entry(short.class, JDBCType.SMALLINT),
89    entry(Integer.class, JDBCType.INTEGER),
90    entry(int.class, JDBCType.INTEGER),
91    entry(Long.class, JDBCType.BIGINT),
92    entry(long.class, JDBCType.BIGINT),
93    entry(Float.class, JDBCType.REAL),
94    entry(float.class, JDBCType.REAL),
95    entry(Double.class, JDBCType.DOUBLE),
96    entry(double.class, JDBCType.DOUBLE),
97    entry(java.math.BigInteger.class, JDBCType.NUMERIC),
98    entry(java.math.BigDecimal.class, JDBCType.DECIMAL)
99    // :~)
100    );
101   
 
102  0 toggle private ParameterSetterFactory() {}
103   
 
104  807 toggle static CustomStatementSetter<?> smartSetterImpl(SetParameterIndex index)
105    {
106  807 var jdbcType = index.meta().jdbcType();
107  807 var typeOfValue = index.typeOfValue();
108   
109  807 var deducedJdbcType = TYPE_TO_JDBC_TYPES.get(typeOfValue);
110  807 if (deducedJdbcType != null) {
111  271 return (stmt, paramIndex, columnMeta, value) ->
112    stmt.setObject(paramIndex, value, deducedJdbcType.getVendorTypeNumber());
113    }
114   
115    /*
116    * Gets setter by type of value
117    */
118  536 var setterFactory = JAVA_TIME_SETTERS.get(typeOfValue);
119  536 if (setterFactory != null) {
120  173 return setterFactory.apply(jdbcType);
121    }
122    // :~)
123   
124    /*
125    * Special processing for JDBCType.OTHER, which is used for some database vendor specific types
126    *
127    * e.g. oracle: timestamp_tz, timestamp_ltz
128    */
129  363 if (jdbcType.equals(JDBCType.OTHER)) {
130  1 var typeName = index.meta().typeName();
131   
132  1 if (typeName.contains("timestamp") || typeName.contains("datetime")) {
133  0 var targetType = typeName.contains("time zone") || typeName.contains("tz")?
134    Types.TIMESTAMP_WITH_TIMEZONE : Types.TIMESTAMP;
135   
136  0 return (stmt, paramIndex, columnMeta, value) ->
137    stmt.setObject(paramIndex, value, targetType);
138    }
139    }
140    // :~)
141   
142  363 return (stmt, paramIndex, columnMeta, value) ->
143    stmt.setObject(paramIndex, value, jdbcType.getVendorTypeNumber());
144    }
145   
146    private static final Map<JDBCType, CustomStatementSetter<OffsetDateTime>> JDBC_TEMPORAL_SETTERS =
147    readonlyMap(
148    entry(
149    JDBCType.DATE,
150    (stmt, paramIndex, columnMeta, value) ->
151    stmt.setDate(paramIndex, java.sql.Date.valueOf(value.toLocalDate()))
152    ),
153    entry(
154    JDBCType.TIME,
155    (stmt, paramIndex, columnMeta, value) ->
156    stmt.setTime(paramIndex, java.sql.Time.valueOf(value.toLocalTime()))
157    ),
158    entry(
159    JDBCType.TIMESTAMP,
160    (stmt, paramIndex, columnMeta, value) ->
161    stmt.setTimestamp(paramIndex, java.sql.Timestamp.valueOf(value.toLocalDateTime()))
162    )
163    );
164   
 
165  171 toggle private static CustomStatementSetter<?> getSetterForGeneralOffsetDateTime(
166    JDBCType jdbcType, Function<Object, OffsetDateTime> toOffsetDateTime
167    ) {
168  171 var setter = JDBC_TEMPORAL_SETTERS.get(jdbcType);
169  171 if (setter == null) {
170  171 setter = (stmt, paramIndex, columnMeta, value) -> stmt.setObject(paramIndex, value);
171    }
172   
173  171 final var finalSetter = setter;
174  171 return (stmt, paramIndex, columnMeta, value) -> {
175  2557 var offsetDateTime = toOffsetDateTime.apply(value);
176  2557 finalSetter.setParameter(stmt, paramIndex, columnMeta, offsetDateTime);
177    };
178    }
179   
 
180  3 toggle @SafeVarargs
181    private static <K, V> Map<K, V> readonlyMap(
182    Map.Entry<K, V>... keyValues
183    ) {
184  3 return Stream.of(keyValues)
185    .collect(Collectors.toMap(
186    entry -> entry.getKey(),
187    entry -> entry.getValue()
188    ));
189    }
190    }
191