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