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