UnknownTypeHandler.java

  1. /*
  2.  *    Copyright 2009-2023 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.sql.CallableStatement;
  18. import java.sql.PreparedStatement;
  19. import java.sql.ResultSet;
  20. import java.sql.ResultSetMetaData;
  21. import java.sql.SQLException;
  22. import java.util.HashMap;
  23. import java.util.Map;
  24. import java.util.function.Supplier;

  25. import org.apache.ibatis.io.Resources;
  26. import org.apache.ibatis.session.Configuration;

  27. /**
  28.  * @author Clinton Begin
  29.  */
  30. public class UnknownTypeHandler extends BaseTypeHandler<Object> {

  31.   private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler();
  32.   // TODO Rename to 'configuration' after removing the 'configuration' property(deprecated property) on parent class
  33.   private final Configuration config;
  34.   private final Supplier<TypeHandlerRegistry> typeHandlerRegistrySupplier;

  35.   /**
  36.    * The constructor that pass a MyBatis configuration.
  37.    *
  38.    * @param configuration
  39.    *          a MyBatis configuration
  40.    *
  41.    * @since 3.5.4
  42.    */
  43.   public UnknownTypeHandler(Configuration configuration) {
  44.     this.config = configuration;
  45.     this.typeHandlerRegistrySupplier = configuration::getTypeHandlerRegistry;
  46.   }

  47.   /**
  48.    * The constructor that pass the type handler registry.
  49.    *
  50.    * @param typeHandlerRegistry
  51.    *          a type handler registry
  52.    *
  53.    * @deprecated Since 3.5.4, please use the {@link #UnknownTypeHandler(Configuration)}.
  54.    */
  55.   @Deprecated
  56.   public UnknownTypeHandler(TypeHandlerRegistry typeHandlerRegistry) {
  57.     this.config = new Configuration();
  58.     this.typeHandlerRegistrySupplier = () -> typeHandlerRegistry;
  59.   }

  60.   @Override
  61.   public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
  62.       throws SQLException {
  63.     TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
  64.     handler.setParameter(ps, i, parameter, jdbcType);
  65.   }

  66.   @Override
  67.   public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
  68.     TypeHandler<?> handler = resolveTypeHandler(rs, columnName);
  69.     return handler.getResult(rs, columnName);
  70.   }

  71.   @Override
  72.   public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
  73.     TypeHandler<?> handler = resolveTypeHandler(rs.getMetaData(), columnIndex);
  74.     if (handler == null || handler instanceof UnknownTypeHandler) {
  75.       handler = OBJECT_TYPE_HANDLER;
  76.     }
  77.     return handler.getResult(rs, columnIndex);
  78.   }

  79.   @Override
  80.   public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
  81.     return cs.getObject(columnIndex);
  82.   }

  83.   private TypeHandler<?> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
  84.     TypeHandler<?> handler;
  85.     if (parameter == null) {
  86.       handler = OBJECT_TYPE_HANDLER;
  87.     } else {
  88.       handler = typeHandlerRegistrySupplier.get().getTypeHandler(parameter.getClass(), jdbcType);
  89.       // check if handler is null (issue #270)
  90.       if (handler == null || handler instanceof UnknownTypeHandler) {
  91.         handler = OBJECT_TYPE_HANDLER;
  92.       }
  93.     }
  94.     return handler;
  95.   }

  96.   private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column) {
  97.     try {
  98.       Map<String, Integer> columnIndexLookup;
  99.       columnIndexLookup = new HashMap<>();
  100.       ResultSetMetaData rsmd = rs.getMetaData();
  101.       int count = rsmd.getColumnCount();
  102.       boolean useColumnLabel = config.isUseColumnLabel();
  103.       for (int i = 1; i <= count; i++) {
  104.         String name = useColumnLabel ? rsmd.getColumnLabel(i) : rsmd.getColumnName(i);
  105.         columnIndexLookup.put(name, i);
  106.       }
  107.       Integer columnIndex = columnIndexLookup.get(column);
  108.       TypeHandler<?> handler = null;
  109.       if (columnIndex != null) {
  110.         handler = resolveTypeHandler(rsmd, columnIndex);
  111.       }
  112.       if (handler == null || handler instanceof UnknownTypeHandler) {
  113.         handler = OBJECT_TYPE_HANDLER;
  114.       }
  115.       return handler;
  116.     } catch (SQLException e) {
  117.       throw new TypeException("Error determining JDBC type for column " + column + ".  Cause: " + e, e);
  118.     }
  119.   }

  120.   private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) {
  121.     TypeHandler<?> handler = null;
  122.     JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
  123.     Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
  124.     if (javaType != null && jdbcType != null) {
  125.       handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType, jdbcType);
  126.     } else if (javaType != null) {
  127.       handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType);
  128.     } else if (jdbcType != null) {
  129.       handler = typeHandlerRegistrySupplier.get().getTypeHandler(jdbcType);
  130.     }
  131.     return handler;
  132.   }

  133.   private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
  134.     try {
  135.       return JdbcType.forCode(rsmd.getColumnType(columnIndex));
  136.     } catch (Exception e) {
  137.       return null;
  138.     }
  139.   }

  140.   private Class<?> safeGetClassForColumn(ResultSetMetaData rsmd, Integer columnIndex) {
  141.     try {
  142.       return Resources.classForName(rsmd.getColumnClassName(columnIndex));
  143.     } catch (Exception e) {
  144.       return null;
  145.     }
  146.   }
  147. }