mybatis/mybatis-3 · error · TypeException
Error determining JDBC type for column {}. Cause: {}
Error message
Error determining JDBC type for column {}. Cause: {} What it means
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.
Source
Thrown at src/main/java/org/apache/ibatis/type/UnknownTypeHandler.java:129
columnIndexLookup = new HashMap<>();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
boolean useColumnLabel = config.isUseColumnLabel();
for (int i = 1; i <= count; i++) {
String name = useColumnLabel ? rsmd.getColumnLabel(i) : rsmd.getColumnName(i);
columnIndexLookup.put(name, i);
}
Integer columnIndex = columnIndexLookup.get(column);
TypeHandler<?> handler = null;
if (columnIndex != null) {
handler = resolveTypeHandler(rsmd, columnIndex);
}
if (handler == null || handler instanceof UnknownTypeHandler) {
handler = ObjectTypeHandler.INSTANCE;
}
return handler;
} catch (SQLException e) {
throw new TypeException("Error determining JDBC type for column " + column + ". Cause: " + e, e);
}
}
private TypeHandler<?> resolveTypeHandler(ResultSetMetaData rsmd, Integer columnIndex) {
TypeHandler<?> handler = null;
JdbcType jdbcType = safeGetJdbcTypeForColumn(rsmd, columnIndex);
Class<?> javaType = safeGetClassForColumn(rsmd, columnIndex);
if (javaType != null && jdbcType != null) {
handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType, jdbcType);
} else if (javaType != null) {
handler = typeHandlerRegistrySupplier.get().getTypeHandler(javaType);
} else if (jdbcType != null) {
handler = typeHandlerRegistrySupplier.get().getTypeHandler(jdbcType);
}
return handler;
}
private JdbcType safeGetJdbcTypeForColumn(ResultSetMetaData rsmd, Integer columnIndex) {View on GitHub (pinned to 008069adb1)
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.
Example fix
<!-- before: property type unknown -> UnknownTypeHandler inspects metadata at runtime --> <resultMap id="rowMap" type="com.example.Row"> <result column="PAYLOAD" property="payload"/> </resultMap> <!-- after: explicit javaType/typeHandler, metadata never queried --> <resultMap id="rowMap" type="com.example.Row"> <result column="PAYLOAD" property="payload" javaType="java.lang.String"/> </resultMap>
Defensive patterns
Strategy: try-catch
Validate before calling
// Before running the query, confirm the driver can supply metadata for this statement's ResultSet
try (PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) {
ResultSetMetaData md = rs.getMetaData(); // throws here, not mid-mapping, if the driver is broken
int n = md.getColumnCount();
for (int i = 1; i <= n; i++) {
md.getColumnLabel(i);
md.getColumnName(i);
}
} catch (SQLException e) {
// fail fast with a clear message about driver/metadata support before MyBatis maps anything
} Type guard
// In the mapper XML/config side: guard the mapping so UnknownTypeHandler is never used
public static boolean mappingIsSafe(ResultMap rm) {
for (ResultMapping m : rm.getResultMappings()) {
if (m.getJavaType() == Object.class && m.getTypeHandler() == null
&& m.getJdbcType() == null) {
return false; // would fall through to UnknownTypeHandler -> runtime metadata lookup
}
}
return true;
} Try / catch
try {
List<Row> rows = sqlSession.selectList("com.example.selectRows");
} catch (org.apache.ibatis.type.TypeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Error determining JDBC type for column")) {
String column = e.getMessage().split("for column ")[1].split("\.\\s+Cause")[0];
throw new IllegalStateException(
"JDBC driver failed to report metadata for column [" + column
+ "] — check driver version / mapping javaType", e.getCause());
}
throw e; // not this error — rethrow
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- SqlRunner could not find a TypeHandler instance for {}
- Could not find type handler for Java type '" + propertyGener
- Could not set parameters for mapping: " + parameterMapping +
- ArrayType Handler requires SQL array or java array parameter
- Error setting non null for parameter #" + i + " with JdbcTyp
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/ffa45d6ab5510088.
Report an issue: GitHub.