View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
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   * @author Clinton Begin
53   * @author Kazuki Shimizu
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     * The default constructor.
68     */
69    public TypeHandlerRegistry() {
70      this(new Configuration());
71    }
72  
73    /**
74     * The constructor that pass the MyBatis configuration.
75     *
76     * @param configuration a MyBatis configuration
77     * @since 3.5.4
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     // issue #273
178     register(Character.class, new CharacterTypeHandler());
179     register(char.class, new CharacterTypeHandler());
180   }
181 
182   /**
183    * Set a default {@link TypeHandler} class for {@link Enum}.
184    * A default {@link TypeHandler} is {@link org.apache.ibatis.type.EnumTypeHandler}.
185    * @param typeHandler a type handler class for {@link Enum}
186    * @since 3.4.5
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         // #591
246         handler = pickSoleHandler(jdbcHandlerMap);
247       }
248     }
249     // type drives generics here
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         // Found a type handler registered to a super interface
283         HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<>();
284         for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
285           // Create a type handler instance with enum type as a constructor arg
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         // More than one type handlers registered.
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   // REGISTER INSTANCE
330   //
331 
332   // Only handler
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     // @since 3.1.0 - try to auto-discover the mapped type
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         // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
352       }
353     }
354     if (!mappedTypeFound) {
355       register((Class<T>) null, typeHandler);
356     }
357   }
358 
359   // java type + handler
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   // java type + jdbc type + handler
384 
385   // Cast is required here
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   // REGISTER CLASS
405   //
406 
407   // Only handler type
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   // java type + handler type
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   // java type + jdbc type + handler type
434 
435   public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
436     register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
437   }
438 
439   // Construct a handler (used also from Builders)
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         // ignored
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   // scan
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       //Ignore inner classes and interfaces (including package-info.java) and abstract classes
469       if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
470         register(type);
471       }
472     }
473   }
474 
475   // get information
476 
477   /**
478    * Gets the type handlers.
479    *
480    * @return the type handlers
481    * @since 3.2.2
482    */
483   public Collection<TypeHandler<?>> getTypeHandlers() {
484     return Collections.unmodifiableCollection(allTypeHandlersMap.values());
485   }
486 
487 }