1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.type;
17
18 import java.io.InputStream;
19 import java.io.Reader;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.Modifier;
22 import java.lang.reflect.Type;
23 import java.math.BigDecimal;
24 import java.math.BigInteger;
25 import java.time.Instant;
26 import java.time.LocalDate;
27 import java.time.LocalDateTime;
28 import java.time.LocalTime;
29 import java.time.Month;
30 import java.time.OffsetDateTime;
31 import java.time.OffsetTime;
32 import java.time.Year;
33 import java.time.YearMonth;
34 import java.time.ZonedDateTime;
35 import java.time.chrono.JapaneseDate;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Date;
39 import java.util.EnumMap;
40 import java.util.HashMap;
41 import java.util.Map;
42 import java.util.Map.Entry;
43 import java.util.Set;
44 import java.util.concurrent.ConcurrentHashMap;
45
46 import org.apache.ibatis.binding.MapperMethod.ParamMap;
47 import org.apache.ibatis.io.ResolverUtil;
48 import org.apache.ibatis.io.Resources;
49 import org.apache.ibatis.session.Configuration;
50
51
52
53
54
55 public final class TypeHandlerRegistry {
56
57 private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
58 private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
59 private final TypeHandler<Object> unknownTypeHandler;
60 private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
61
62 private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
63
64 private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
65
66
67
68
69 public TypeHandlerRegistry() {
70 this(new Configuration());
71 }
72
73
74
75
76
77
78
79 public TypeHandlerRegistry(Configuration configuration) {
80 this.unknownTypeHandler = new UnknownTypeHandler(configuration);
81
82 register(Boolean.class, new BooleanTypeHandler());
83 register(boolean.class, new BooleanTypeHandler());
84 register(JdbcType.BOOLEAN, new BooleanTypeHandler());
85 register(JdbcType.BIT, new BooleanTypeHandler());
86
87 register(Byte.class, new ByteTypeHandler());
88 register(byte.class, new ByteTypeHandler());
89 register(JdbcType.TINYINT, new ByteTypeHandler());
90
91 register(Short.class, new ShortTypeHandler());
92 register(short.class, new ShortTypeHandler());
93 register(JdbcType.SMALLINT, new ShortTypeHandler());
94
95 register(Integer.class, new IntegerTypeHandler());
96 register(int.class, new IntegerTypeHandler());
97 register(JdbcType.INTEGER, new IntegerTypeHandler());
98
99 register(Long.class, new LongTypeHandler());
100 register(long.class, new LongTypeHandler());
101
102 register(Float.class, new FloatTypeHandler());
103 register(float.class, new FloatTypeHandler());
104 register(JdbcType.FLOAT, new FloatTypeHandler());
105
106 register(Double.class, new DoubleTypeHandler());
107 register(double.class, new DoubleTypeHandler());
108 register(JdbcType.DOUBLE, new DoubleTypeHandler());
109
110 register(Reader.class, new ClobReaderTypeHandler());
111 register(String.class, new StringTypeHandler());
112 register(String.class, JdbcType.CHAR, new StringTypeHandler());
113 register(String.class, JdbcType.CLOB, new ClobTypeHandler());
114 register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
115 register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
116 register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
117 register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
118 register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
119 register(JdbcType.CHAR, new StringTypeHandler());
120 register(JdbcType.VARCHAR, new StringTypeHandler());
121 register(JdbcType.CLOB, new ClobTypeHandler());
122 register(JdbcType.LONGVARCHAR, new StringTypeHandler());
123 register(JdbcType.NVARCHAR, new NStringTypeHandler());
124 register(JdbcType.NCHAR, new NStringTypeHandler());
125 register(JdbcType.NCLOB, new NClobTypeHandler());
126
127 register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
128 register(JdbcType.ARRAY, new ArrayTypeHandler());
129
130 register(BigInteger.class, new BigIntegerTypeHandler());
131 register(JdbcType.BIGINT, new LongTypeHandler());
132
133 register(BigDecimal.class, new BigDecimalTypeHandler());
134 register(JdbcType.REAL, new BigDecimalTypeHandler());
135 register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
136 register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
137
138 register(InputStream.class, new BlobInputStreamTypeHandler());
139 register(Byte[].class, new ByteObjectArrayTypeHandler());
140 register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
141 register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
142 register(byte[].class, new ByteArrayTypeHandler());
143 register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
144 register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
145 register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
146 register(JdbcType.BLOB, new BlobTypeHandler());
147
148 register(Object.class, unknownTypeHandler);
149 register(Object.class, JdbcType.OTHER, unknownTypeHandler);
150 register(JdbcType.OTHER, unknownTypeHandler);
151
152 register(Date.class, new DateTypeHandler());
153 register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
154 register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
155 register(JdbcType.TIMESTAMP, new DateTypeHandler());
156 register(JdbcType.DATE, new DateOnlyTypeHandler());
157 register(JdbcType.TIME, new TimeOnlyTypeHandler());
158
159 register(java.sql.Date.class, new SqlDateTypeHandler());
160 register(java.sql.Time.class, new SqlTimeTypeHandler());
161 register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
162
163 register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
164
165 register(Instant.class, new InstantTypeHandler());
166 register(LocalDateTime.class, new LocalDateTimeTypeHandler());
167 register(LocalDate.class, new LocalDateTypeHandler());
168 register(LocalTime.class, new LocalTimeTypeHandler());
169 register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
170 register(OffsetTime.class, new OffsetTimeTypeHandler());
171 register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
172 register(Month.class, new MonthTypeHandler());
173 register(Year.class, new YearTypeHandler());
174 register(YearMonth.class, new YearMonthTypeHandler());
175 register(JapaneseDate.class, new JapaneseDateTypeHandler());
176
177
178 register(Character.class, new CharacterTypeHandler());
179 register(char.class, new CharacterTypeHandler());
180 }
181
182
183
184
185
186
187
188 public void setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler) {
189 this.defaultEnumTypeHandler = typeHandler;
190 }
191
192 public boolean hasTypeHandler(Class<?> javaType) {
193 return hasTypeHandler(javaType, null);
194 }
195
196 public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
197 return hasTypeHandler(javaTypeReference, null);
198 }
199
200 public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
201 return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
202 }
203
204 public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
205 return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
206 }
207
208 public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
209 return allTypeHandlersMap.get(handlerType);
210 }
211
212 public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
213 return getTypeHandler((Type) type, null);
214 }
215
216 public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
217 return getTypeHandler(javaTypeReference, null);
218 }
219
220 public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
221 return jdbcTypeHandlerMap.get(jdbcType);
222 }
223
224 public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
225 return getTypeHandler((Type) type, jdbcType);
226 }
227
228 public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
229 return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
230 }
231
232 @SuppressWarnings("unchecked")
233 private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
234 if (ParamMap.class.equals(type)) {
235 return null;
236 }
237 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
238 TypeHandler<?> handler = null;
239 if (jdbcHandlerMap != null) {
240 handler = jdbcHandlerMap.get(jdbcType);
241 if (handler == null) {
242 handler = jdbcHandlerMap.get(null);
243 }
244 if (handler == null) {
245
246 handler = pickSoleHandler(jdbcHandlerMap);
247 }
248 }
249
250 return (TypeHandler<T>) handler;
251 }
252
253 private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
254 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
255 if (jdbcHandlerMap != null) {
256 return NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap) ? null : jdbcHandlerMap;
257 }
258 if (type instanceof Class) {
259 Class<?> clazz = (Class<?>) type;
260 if (Enum.class.isAssignableFrom(clazz)) {
261 Class<?> enumClass = clazz.isAnonymousClass() ? clazz.getSuperclass() : clazz;
262 jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(enumClass, enumClass);
263 if (jdbcHandlerMap == null) {
264 register(enumClass, getInstance(enumClass, defaultEnumTypeHandler));
265 return typeHandlerMap.get(enumClass);
266 }
267 } else {
268 jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
269 }
270 }
271 typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
272 return jdbcHandlerMap;
273 }
274
275 private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
276 for (Class<?> iface : clazz.getInterfaces()) {
277 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(iface);
278 if (jdbcHandlerMap == null) {
279 jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
280 }
281 if (jdbcHandlerMap != null) {
282
283 HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<>();
284 for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
285
286 newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
287 }
288 return newMap;
289 }
290 }
291 return null;
292 }
293
294 private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
295 Class<?> superclass = clazz.getSuperclass();
296 if (superclass == null || Object.class.equals(superclass)) {
297 return null;
298 }
299 Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);
300 if (jdbcHandlerMap != null) {
301 return jdbcHandlerMap;
302 } else {
303 return getJdbcHandlerMapForSuperclass(superclass);
304 }
305 }
306
307 private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
308 TypeHandler<?> soleHandler = null;
309 for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
310 if (soleHandler == null) {
311 soleHandler = handler;
312 } else if (!handler.getClass().equals(soleHandler.getClass())) {
313
314 return null;
315 }
316 }
317 return soleHandler;
318 }
319
320 public TypeHandler<Object> getUnknownTypeHandler() {
321 return unknownTypeHandler;
322 }
323
324 public void register(JdbcType jdbcType, TypeHandler<?> handler) {
325 jdbcTypeHandlerMap.put(jdbcType, handler);
326 }
327
328
329
330
331
332
333
334 @SuppressWarnings("unchecked")
335 public <T> void register(TypeHandler<T> typeHandler) {
336 boolean mappedTypeFound = false;
337 MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
338 if (mappedTypes != null) {
339 for (Class<?> handledType : mappedTypes.value()) {
340 register(handledType, typeHandler);
341 mappedTypeFound = true;
342 }
343 }
344
345 if (!mappedTypeFound && typeHandler instanceof TypeReference) {
346 try {
347 TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
348 register(typeReference.getRawType(), typeHandler);
349 mappedTypeFound = true;
350 } catch (Throwable t) {
351
352 }
353 }
354 if (!mappedTypeFound) {
355 register((Class<T>) null, typeHandler);
356 }
357 }
358
359
360
361 public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
362 register((Type) javaType, typeHandler);
363 }
364
365 private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
366 MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
367 if (mappedJdbcTypes != null) {
368 for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
369 register(javaType, handledJdbcType, typeHandler);
370 }
371 if (mappedJdbcTypes.includeNullJdbcType()) {
372 register(javaType, null, typeHandler);
373 }
374 } else {
375 register(javaType, null, typeHandler);
376 }
377 }
378
379 public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
380 register(javaTypeReference.getRawType(), handler);
381 }
382
383
384
385
386 @SuppressWarnings("cast")
387 public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
388 register((Type) type, jdbcType, handler);
389 }
390
391 private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
392 if (javaType != null) {
393 Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
394 if (map == null || map == NULL_TYPE_HANDLER_MAP) {
395 map = new HashMap<>();
396 }
397 map.put(jdbcType, handler);
398 typeHandlerMap.put(javaType, map);
399 }
400 allTypeHandlersMap.put(handler.getClass(), handler);
401 }
402
403
404
405
406
407
408
409 public void register(Class<?> typeHandlerClass) {
410 boolean mappedTypeFound = false;
411 MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
412 if (mappedTypes != null) {
413 for (Class<?> javaTypeClass : mappedTypes.value()) {
414 register(javaTypeClass, typeHandlerClass);
415 mappedTypeFound = true;
416 }
417 }
418 if (!mappedTypeFound) {
419 register(getInstance(null, typeHandlerClass));
420 }
421 }
422
423
424
425 public void register(String javaTypeClassName, String typeHandlerClassName) throws ClassNotFoundException {
426 register(Resources.classForName(javaTypeClassName), Resources.classForName(typeHandlerClassName));
427 }
428
429 public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
430 register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
431 }
432
433
434
435 public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
436 register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
437 }
438
439
440
441 @SuppressWarnings("unchecked")
442 public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
443 if (javaTypeClass != null) {
444 try {
445 Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
446 return (TypeHandler<T>) c.newInstance(javaTypeClass);
447 } catch (NoSuchMethodException ignored) {
448
449 } catch (Exception e) {
450 throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
451 }
452 }
453 try {
454 Constructor<?> c = typeHandlerClass.getConstructor();
455 return (TypeHandler<T>) c.newInstance();
456 } catch (Exception e) {
457 throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
458 }
459 }
460
461
462
463 public void register(String packageName) {
464 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
465 resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
466 Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
467 for (Class<?> type : handlerSet) {
468
469 if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
470 register(type);
471 }
472 }
473 }
474
475
476
477
478
479
480
481
482
483 public Collection<TypeHandler<?>> getTypeHandlers() {
484 return Collections.unmodifiableCollection(allTypeHandlersMap.values());
485 }
486
487 }