TypeHandlerRegistry.java

  1. /*
  2.  *    Copyright 2009-2024 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.  *       https://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. import java.io.InputStream;
  18. import java.io.Reader;
  19. import java.lang.reflect.Constructor;
  20. import java.lang.reflect.Modifier;
  21. import java.lang.reflect.Type;
  22. import java.math.BigDecimal;
  23. import java.math.BigInteger;
  24. import java.sql.Time;
  25. import java.sql.Timestamp;
  26. import java.time.Instant;
  27. import java.time.LocalDate;
  28. import java.time.LocalDateTime;
  29. import java.time.LocalTime;
  30. import java.time.Month;
  31. import java.time.OffsetDateTime;
  32. import java.time.OffsetTime;
  33. import java.time.Year;
  34. import java.time.YearMonth;
  35. import java.time.ZonedDateTime;
  36. import java.time.chrono.JapaneseDate;
  37. import java.util.Collection;
  38. import java.util.Collections;
  39. import java.util.Date;
  40. import java.util.EnumMap;
  41. import java.util.HashMap;
  42. import java.util.Map;
  43. import java.util.Map.Entry;
  44. import java.util.Set;
  45. import java.util.concurrent.ConcurrentHashMap;

  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.  * @author Clinton Begin
  52.  * @author Kazuki Shimizu
  53.  */
  54. public final class TypeHandlerRegistry {

  55.   private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
  56.   private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
  57.   private final TypeHandler<Object> unknownTypeHandler;
  58.   private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();

  59.   private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

  60.   private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

  61.   /**
  62.    * The default constructor.
  63.    */
  64.   public TypeHandlerRegistry() {
  65.     this(new Configuration());
  66.   }

  67.   /**
  68.    * The constructor that pass the MyBatis configuration.
  69.    *
  70.    * @param configuration
  71.    *          a MyBatis configuration
  72.    *
  73.    * @since 3.5.4
  74.    */
  75.   public TypeHandlerRegistry(Configuration configuration) {
  76.     this.unknownTypeHandler = new UnknownTypeHandler(configuration);

  77.     register(Boolean.class, new BooleanTypeHandler());
  78.     register(boolean.class, new BooleanTypeHandler());
  79.     register(JdbcType.BOOLEAN, new BooleanTypeHandler());
  80.     register(JdbcType.BIT, new BooleanTypeHandler());

  81.     register(Byte.class, new ByteTypeHandler());
  82.     register(byte.class, new ByteTypeHandler());
  83.     register(JdbcType.TINYINT, new ByteTypeHandler());

  84.     register(Short.class, new ShortTypeHandler());
  85.     register(short.class, new ShortTypeHandler());
  86.     register(JdbcType.SMALLINT, new ShortTypeHandler());

  87.     register(Integer.class, new IntegerTypeHandler());
  88.     register(int.class, new IntegerTypeHandler());
  89.     register(JdbcType.INTEGER, new IntegerTypeHandler());

  90.     register(Long.class, new LongTypeHandler());
  91.     register(long.class, new LongTypeHandler());

  92.     register(Float.class, new FloatTypeHandler());
  93.     register(float.class, new FloatTypeHandler());
  94.     register(JdbcType.FLOAT, new FloatTypeHandler());

  95.     register(Double.class, new DoubleTypeHandler());
  96.     register(double.class, new DoubleTypeHandler());
  97.     register(JdbcType.DOUBLE, new DoubleTypeHandler());

  98.     register(Reader.class, new ClobReaderTypeHandler());
  99.     register(String.class, new StringTypeHandler());
  100.     register(String.class, JdbcType.CHAR, new StringTypeHandler());
  101.     register(String.class, JdbcType.CLOB, new ClobTypeHandler());
  102.     register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
  103.     register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
  104.     register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
  105.     register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
  106.     register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
  107.     register(JdbcType.CHAR, new StringTypeHandler());
  108.     register(JdbcType.VARCHAR, new StringTypeHandler());
  109.     register(JdbcType.CLOB, new ClobTypeHandler());
  110.     register(JdbcType.LONGVARCHAR, new StringTypeHandler());
  111.     register(JdbcType.NVARCHAR, new NStringTypeHandler());
  112.     register(JdbcType.NCHAR, new NStringTypeHandler());
  113.     register(JdbcType.NCLOB, new NClobTypeHandler());

  114.     register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
  115.     register(JdbcType.ARRAY, new ArrayTypeHandler());

  116.     register(BigInteger.class, new BigIntegerTypeHandler());
  117.     register(JdbcType.BIGINT, new LongTypeHandler());

  118.     register(BigDecimal.class, new BigDecimalTypeHandler());
  119.     register(JdbcType.REAL, new BigDecimalTypeHandler());
  120.     register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
  121.     register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

  122.     register(InputStream.class, new BlobInputStreamTypeHandler());
  123.     register(Byte[].class, new ByteObjectArrayTypeHandler());
  124.     register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
  125.     register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
  126.     register(byte[].class, new ByteArrayTypeHandler());
  127.     register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
  128.     register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
  129.     register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
  130.     register(JdbcType.BLOB, new BlobTypeHandler());

  131.     register(Object.class, unknownTypeHandler);
  132.     register(Object.class, JdbcType.OTHER, unknownTypeHandler);
  133.     register(JdbcType.OTHER, unknownTypeHandler);

  134.     register(Date.class, new DateTypeHandler());
  135.     register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
  136.     register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
  137.     register(JdbcType.TIMESTAMP, new DateTypeHandler());
  138.     register(JdbcType.DATE, new DateOnlyTypeHandler());
  139.     register(JdbcType.TIME, new TimeOnlyTypeHandler());

  140.     register(java.sql.Date.class, new SqlDateTypeHandler());
  141.     register(Time.class, new SqlTimeTypeHandler());
  142.     register(Timestamp.class, new SqlTimestampTypeHandler());

  143.     register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

  144.     register(Instant.class, new InstantTypeHandler());
  145.     register(LocalDateTime.class, new LocalDateTimeTypeHandler());
  146.     register(LocalDate.class, new LocalDateTypeHandler());
  147.     register(LocalTime.class, new LocalTimeTypeHandler());
  148.     register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
  149.     register(OffsetTime.class, new OffsetTimeTypeHandler());
  150.     register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
  151.     register(Month.class, new MonthTypeHandler());
  152.     register(Year.class, new YearTypeHandler());
  153.     register(YearMonth.class, new YearMonthTypeHandler());
  154.     register(JapaneseDate.class, new JapaneseDateTypeHandler());

  155.     // issue #273
  156.     register(Character.class, new CharacterTypeHandler());
  157.     register(char.class, new CharacterTypeHandler());
  158.   }

  159.   /**
  160.    * Set a default {@link TypeHandler} class for {@link Enum}. A default {@link TypeHandler} is
  161.    * {@link org.apache.ibatis.type.EnumTypeHandler}.
  162.    *
  163.    * @param typeHandler
  164.    *          a type handler class for {@link Enum}
  165.    *
  166.    * @since 3.4.5
  167.    */
  168.   public void setDefaultEnumTypeHandler(Class<? extends TypeHandler> typeHandler) {
  169.     this.defaultEnumTypeHandler = typeHandler;
  170.   }

  171.   public boolean hasTypeHandler(Class<?> javaType) {
  172.     return hasTypeHandler(javaType, null);
  173.   }

  174.   public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
  175.     return hasTypeHandler(javaTypeReference, null);
  176.   }

  177.   public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
  178.     return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
  179.   }

  180.   public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
  181.     return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
  182.   }

  183.   public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
  184.     return allTypeHandlersMap.get(handlerType);
  185.   }

  186.   public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
  187.     return getTypeHandler((Type) type, null);
  188.   }

  189.   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
  190.     return getTypeHandler(javaTypeReference, null);
  191.   }

  192.   public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
  193.     return jdbcTypeHandlerMap.get(jdbcType);
  194.   }

  195.   public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
  196.     return getTypeHandler((Type) type, jdbcType);
  197.   }

  198.   public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
  199.     return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
  200.   }

  201.   @SuppressWarnings("unchecked")
  202.   private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
  203.     if (ParamMap.class.equals(type)) {
  204.       return null;
  205.     }
  206.     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
  207.     TypeHandler<?> handler = null;
  208.     if (jdbcHandlerMap != null) {
  209.       handler = jdbcHandlerMap.get(jdbcType);
  210.       if (handler == null) {
  211.         handler = jdbcHandlerMap.get(null);
  212.       }
  213.       if (handler == null) {
  214.         // #591
  215.         handler = pickSoleHandler(jdbcHandlerMap);
  216.       }
  217.     }
  218.     // type drives generics here
  219.     return (TypeHandler<T>) handler;
  220.   }

  221.   private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
  222.     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
  223.     if (jdbcHandlerMap != null) {
  224.       return NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap) ? null : jdbcHandlerMap;
  225.     }
  226.     if (type instanceof Class) {
  227.       Class<?> clazz = (Class<?>) type;
  228.       if (Enum.class.isAssignableFrom(clazz)) {
  229.         if (clazz.isAnonymousClass()) {
  230.           return getJdbcHandlerMap(clazz.getSuperclass());
  231.         }
  232.         jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz);
  233.         if (jdbcHandlerMap == null) {
  234.           register(clazz, getInstance(clazz, defaultEnumTypeHandler));
  235.           return typeHandlerMap.get(clazz);
  236.         }
  237.       } else {
  238.         jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
  239.       }
  240.     }
  241.     typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
  242.     return jdbcHandlerMap;
  243.   }

  244.   private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
  245.     for (Class<?> iface : clazz.getInterfaces()) {
  246.       Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(iface);
  247.       if (jdbcHandlerMap == null) {
  248.         jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
  249.       }
  250.       if (jdbcHandlerMap != null) {
  251.         // Found a type handler registered to a super interface
  252.         HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<>();
  253.         for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
  254.           // Create a type handler instance with enum type as a constructor arg
  255.           newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
  256.         }
  257.         return newMap;
  258.       }
  259.     }
  260.     return null;
  261.   }

  262.   private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
  263.     Class<?> superclass = clazz.getSuperclass();
  264.     if (superclass == null || Object.class.equals(superclass)) {
  265.       return null;
  266.     }
  267.     Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(superclass);
  268.     if (jdbcHandlerMap != null) {
  269.       return jdbcHandlerMap;
  270.     }
  271.     return getJdbcHandlerMapForSuperclass(superclass);
  272.   }

  273.   private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
  274.     TypeHandler<?> soleHandler = null;
  275.     for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
  276.       if (soleHandler == null) {
  277.         soleHandler = handler;
  278.       } else if (!handler.getClass().equals(soleHandler.getClass())) {
  279.         // More than one type handlers registered.
  280.         return null;
  281.       }
  282.     }
  283.     return soleHandler;
  284.   }

  285.   public TypeHandler<Object> getUnknownTypeHandler() {
  286.     return unknownTypeHandler;
  287.   }

  288.   public void register(JdbcType jdbcType, TypeHandler<?> handler) {
  289.     jdbcTypeHandlerMap.put(jdbcType, handler);
  290.   }

  291.   //
  292.   // REGISTER INSTANCE
  293.   //

  294.   // Only handler

  295.   @SuppressWarnings("unchecked")
  296.   public <T> void register(TypeHandler<T> typeHandler) {
  297.     boolean mappedTypeFound = false;
  298.     MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);
  299.     if (mappedTypes != null) {
  300.       for (Class<?> handledType : mappedTypes.value()) {
  301.         register(handledType, typeHandler);
  302.         mappedTypeFound = true;
  303.       }
  304.     }
  305.     // @since 3.1.0 - try to auto-discover the mapped type
  306.     if (!mappedTypeFound && typeHandler instanceof TypeReference) {
  307.       try {
  308.         TypeReference<T> typeReference = (TypeReference<T>) typeHandler;
  309.         register(typeReference.getRawType(), typeHandler);
  310.         mappedTypeFound = true;
  311.       } catch (Throwable t) {
  312.         // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
  313.       }
  314.     }
  315.     if (!mappedTypeFound) {
  316.       register((Class<T>) null, typeHandler);
  317.     }
  318.   }

  319.   // java type + handler

  320.   public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
  321.     register((Type) javaType, typeHandler);
  322.   }

  323.   private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
  324.     MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
  325.     if (mappedJdbcTypes != null) {
  326.       for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
  327.         register(javaType, handledJdbcType, typeHandler);
  328.       }
  329.       if (mappedJdbcTypes.includeNullJdbcType()) {
  330.         register(javaType, null, typeHandler);
  331.       }
  332.     } else {
  333.       register(javaType, null, typeHandler);
  334.     }
  335.   }

  336.   public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
  337.     register(javaTypeReference.getRawType(), handler);
  338.   }

  339.   // java type + jdbc type + handler

  340.   // Cast is required here
  341.   @SuppressWarnings("cast")
  342.   public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
  343.     register((Type) type, jdbcType, handler);
  344.   }

  345.   private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  346.     if (javaType != null) {
  347.       Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
  348.       if (map == null || map == NULL_TYPE_HANDLER_MAP) {
  349.         map = new HashMap<>();
  350.       }
  351.       map.put(jdbcType, handler);
  352.       typeHandlerMap.put(javaType, map);
  353.     }
  354.     allTypeHandlersMap.put(handler.getClass(), handler);
  355.   }

  356.   //
  357.   // REGISTER CLASS
  358.   //

  359.   // Only handler type

  360.   public void register(Class<?> typeHandlerClass) {
  361.     boolean mappedTypeFound = false;
  362.     MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
  363.     if (mappedTypes != null) {
  364.       for (Class<?> javaTypeClass : mappedTypes.value()) {
  365.         register(javaTypeClass, typeHandlerClass);
  366.         mappedTypeFound = true;
  367.       }
  368.     }
  369.     if (!mappedTypeFound) {
  370.       register(getInstance(null, typeHandlerClass));
  371.     }
  372.   }

  373.   // java type + handler type

  374.   public void register(String javaTypeClassName, String typeHandlerClassName) throws ClassNotFoundException {
  375.     register(Resources.classForName(javaTypeClassName), Resources.classForName(typeHandlerClassName));
  376.   }

  377.   public void register(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  378.     register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
  379.   }

  380.   // java type + jdbc type + handler type

  381.   public void register(Class<?> javaTypeClass, JdbcType jdbcType, Class<?> typeHandlerClass) {
  382.     register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
  383.   }

  384.   // Construct a handler (used also from Builders)

  385.   @SuppressWarnings("unchecked")
  386.   public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
  387.     if (javaTypeClass != null) {
  388.       try {
  389.         Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
  390.         return (TypeHandler<T>) c.newInstance(javaTypeClass);
  391.       } catch (NoSuchMethodException ignored) {
  392.         // ignored
  393.       } catch (Exception e) {
  394.         throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
  395.       }
  396.     }
  397.     try {
  398.       Constructor<?> c = typeHandlerClass.getConstructor();
  399.       return (TypeHandler<T>) c.newInstance();
  400.     } catch (Exception e) {
  401.       throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
  402.     }
  403.   }

  404.   // scan

  405.   public void register(String packageName) {
  406.     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  407.     resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
  408.     Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
  409.     for (Class<?> type : handlerSet) {
  410.       // Ignore inner classes and interfaces (including package-info.java) and abstract classes
  411.       if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
  412.         register(type);
  413.       }
  414.     }
  415.   }

  416.   // get information

  417.   /**
  418.    * Gets the type handlers.
  419.    *
  420.    * @return the type handlers
  421.    *
  422.    * @since 3.2.2
  423.    */
  424.   public Collection<TypeHandler<?>> getTypeHandlers() {
  425.     return Collections.unmodifiableCollection(allTypeHandlersMap.values());
  426.   }

  427. }