{"record":{"id":"ffa45d6ab5510088","repo":"mybatis/mybatis-3","slug":"error-determining-jdbc-type-for-column-cause","errorCode":null,"errorMessage":"Error determining JDBC type for column {}.  Cause: {}","messagePattern":"Error determining JDBC type for column (.+?)\\.  Cause: (.+?)","errorType":"exception","errorClass":"TypeException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/type/UnknownTypeHandler.java","lineNumber":129,"sourceCode":"      columnIndexLookup = new HashMap<>();\n      ResultSetMetaData rsmd = rs.getMetaData();\n      int count = rsmd.getColumnCount();\n      boolean useColumnLabel = config.isUseColumnLabel();\n      for (int i = 1; i <= count; i++) {\n        String name = useColumnLabel ? rsmd.getColumnLabel(i) : rsmd.getColumnName(i);\n        columnIndexLookup.put(name, i);\n      }\n      Integer columnIndex = columnIndexLookup.get(column);\n      TypeHandler<?> handler = null;\n      if (columnIndex != null) {\n        handler = resolveTypeHandler(rsmd, columnIndex);\n      }\n      if (handler == null || handler instanceof UnknownTypeHandler) {\n        handler = ObjectTypeHandler.INSTANCE;\n      }\n      return handler;\n    } catch (SQLException e) {\n      throw new TypeException(\"Error determining JDBC type for column \" + column + \".  Cause: \" + e, e);\n    }\n  }\n\n  private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) {\n    TypeHandler<?> handler = null;\n    JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);\n    Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);\n    if (javaType != null && jdbcType != null) {\n      handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType, jdbcType);\n    } else if (javaType != null) {\n      handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType);\n    } else if (jdbcType != null) {\n      handler = typeHandlerRegistrySupplier.get().getTypeHandler(jdbcType);\n    }\n    return handler;\n  }\n\n  private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, Integer columnIndex) {","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/type/UnknownTypeHandler.java#L111-L147","documentation":"MyBatis throws this TypeException from UnknownTypeHandler when it must pick a TypeHandler at runtime by inspecting ResultSetMetaData (because no handler could be determined statically for the property) and the JDBC driver raises SQLException while supplying that metadata. The handler builds a column-name -> index lookup via rs.getMetaData(), getColumnCount(), and getColumnLabel()/getColumnName(), then resolves the JDBC/Java type for the column. Any SQLException from those driver calls is wrapped in TypeException with the offending column name, meaning the failure is in the driver/metadata layer, not in MyBatis' type registry itself. Note the per-column type lookups (getColumnType/getColumnClassName) are wrapped in safe* methods that swallow errors, so the surviving throwers are getMetaData(), getColumnCount(), and the label/name iteration.","triggerScenarios":"A result mapping where the property has no javaType, no explicit typeHandler, and no matching registered handler, so UnknownTypeHandler.getNullableResult(rs, columnName) is invoked (e.g. mapping to Object or a property whose type has no registered handler). Then one of these SQLExceptions fires: (1) rs.getMetaData() on a closed or already-advanced streaming ResultSet; (2) rsmd.getColumnCount()/getColumnLabel(i) failing on drivers with limited metadata support (some streaming/cursor modes, forward-only cursors, certain Oracle/MySQL/SQLite driver versions); (3) the ResultSet being accessed concurrently or after the statement was closed; (4) a column label the driver cannot resolve during metadata iteration.","commonSituations":"Mapping a column to an Object property (resultType=\"map\" with unknown JDBC types is a cousin but goes through a different path; this one is unknown property types); using streaming ResultSets (MySQL fetchSize=Integer.MIN_VALUE, Oracle cursor streaming) where metadata is unavailable mid-iteration; upgrading a JDBC driver that changed metadata behavior; using outdated SQLite/Informix/Derby drivers that throw on getColumnClassName or getColumnName for exotic types; closing the SqlSession/ResultSet in another thread while results are still being mapped; complex column labels (aliases, expressions) that drivers report inconsistently between getColumnName and getColumnLabel when useColumnLabel is toggled.","solutions":["Upgrade the JDBC driver to the latest version matching your DB — most metadata SQLExceptions (getColumnType/getColumnLabel failing on specific types) are driver bugs fixed in newer releases.","Give MyBatis static type information so UnknownTypeHandler is never consulted: set javaType on the <result>/<id> mapping, or specify typeHandler explicitly, or map to a concrete property type that has a registered handler.","If using streaming/cursor ResultSets, switch to regular fetch-size pagination (e.g. MySQL statement fetchSize > 0 instead of Integer.MIN_VALUE) so ResultSetMetaData stays available while rows are read.","Check that the SqlSession/ResultSet is still open when results are mapped — remove any code that closes the session in a finally block before iteration completes, or that shares a session across threads.","If the driver throws on the label/alias form, try switching configuration setting useColumnLabel (Configuration.setUseColumnLabel) so the lookup iterates getColumnName vs getColumnLabel, whichever your driver supports.","As a last resort, register a custom TypeHandler for the problematic column's type (TypeHandlerRegistry.register) so resolution never falls through to UnknownTypeHandler."],"exampleFix":"<!-- before: property type unknown -> UnknownTypeHandler inspects metadata at runtime -->\n<resultMap id=\"rowMap\" type=\"com.example.Row\">\n  <result column=\"PAYLOAD\" property=\"payload\"/>\n</resultMap>\n\n<!-- after: explicit javaType/typeHandler, metadata never queried -->\n<resultMap id=\"rowMap\" type=\"com.example.Row\">\n  <result column=\"PAYLOAD\" property=\"payload\" javaType=\"java.lang.String\"/>\n</resultMap>","handlingStrategy":"try-catch","validationCode":"// Before running the query, confirm the driver can supply metadata for this statement's ResultSet\ntry (PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) {\n  ResultSetMetaData md = rs.getMetaData();          // throws here, not mid-mapping, if the driver is broken\n  int n = md.getColumnCount();\n  for (int i = 1; i <= n; i++) {\n    md.getColumnLabel(i);\n    md.getColumnName(i);\n  }\n} catch (SQLException e) {\n  // fail fast with a clear message about driver/metadata support before MyBatis maps anything\n}","typeGuard":"// In the mapper XML/config side: guard the mapping so UnknownTypeHandler is never used\npublic static boolean mappingIsSafe(ResultMap rm) {\n  for (ResultMapping m : rm.getResultMappings()) {\n    if (m.getJavaType() == Object.class && m.getTypeHandler() == null\n        && m.getJdbcType() == null) {\n      return false; // would fall through to UnknownTypeHandler -> runtime metadata lookup\n    }\n  }\n  return true;\n}","tryCatchPattern":"try {\n  List<Row> rows = sqlSession.selectList(\"com.example.selectRows\");\n} catch (org.apache.ibatis.type.TypeException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Error determining JDBC type for column\")) {\n    String column = e.getMessage().split(\"for column \")[1].split(\"\\.\\\\s+Cause\")[0];\n    throw new IllegalStateException(\n        \"JDBC driver failed to report metadata for column [\" + column\n        + \"] — check driver version / mapping javaType\", e.getCause());\n  }\n  throw e; // not this error — rethrow\n}","preventionTips":["Always declare javaType (or a concrete property type) on result mappings for columns with unusual JDBC types, so handler resolution is static and never consults ResultSetMetaData.","Pin a recent, DB-matching JDBC driver version and add a startup smoke test that reads ResultSetMetaData from a sample query.","Avoid streaming ResultSets (MySQL fetchSize=Integer.MIN_VALUE, Oracle prefetch modes) unless you have verified getMetaData() works during row iteration on your driver.","Never close a SqlSession or share it across threads while result mapping is in progress.","For exotic column types (JSON, arrays, geo types), register a dedicated TypeHandler in the configuration instead of relying on auto-detection.","Test mappings against the exact database and driver used in production — metadata behavior differs across drivers and versions."],"tags":["mybatis","jdbc","type-handler","resultset-metadata","driver-compatibility","typeexception"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}