mybatis/mybatis-3 · warning · SQLException
${className} is not a wrapper.
Error message
${className} is not a wrapper. What it means
UnpooledDataSource implements java.sql.Wrapper but wraps nothing, so unwrap(Class<T>) unconditionally throws SQLException '<ClassName> is not a wrapper.' and isWrapperFor(Class) returns false. This is the standard way for non-proxy JDBC objects to satisfy the Wrapper contract. Note the inner DriverProxy class does delegate unwrap/isWrapperFor to the real driver — the exception comes only from calls on the DataSource itself.
Source
Thrown at src/main/java/org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java:312
@Override
public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
return this.driver.getPropertyInfo(u, p);
}
@Override
public boolean jdbcCompliant() {
return this.driver.jdbcCompliant();
}
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException(getClass().getName() + " is not a wrapper.");
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public Logger getParentLogger() {
// requires JDK version 1.6
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
View on GitHub (pinned to 008069adb1)
Solutions
- Call isWrapperFor(iface) first and skip unwrap when it returns false
- Unwrap the java.sql.Connection from getConnection() (or its DriverProxy) for driver-specific access instead of the DataSource
- Switch to an external pool DataSource (HikariCP/DBCP) wired through a custom DataSourceFactory if you need DataSource-level unwrapping
Example fix
// before
OracleDataSource ods = ds.unwrap(OracleDataSource.class); // throws
// after
if (ds.isWrapperFor(OracleDataSource.class)) {
OracleDataSource ods = ds.unwrap(OracleDataSource.class);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (ds.isWrapperFor(targetType)) {
T t = ds.unwrap(targetType);
} else {
// UNPOOLED DataSource wraps nothing — use instanceof on concrete type instead
} Type guard
static <T> Optional<T> unwrapIfPossible(javax.sql.DataSource ds, Class<T> type) {
try {
return ds.isWrapperFor(type) ? Optional.of(ds.unwrap(type)) : Optional.empty();
} catch (SQLException e) {
return Optional.empty();
}
} Try / catch
try {
return ds.unwrap(type);
} catch (SQLException e) {
if (e.getMessage().endsWith("is not a wrapper.")) return Optional.empty();
throw e;
} Prevention
- Guard every unwrap with isWrapperFor
- Detect concrete pool types with instanceof (HikariDataSource etc.) rather than unwrap on MyBatis built-ins
When it happens
Trigger: Calling dataSource.unwrap(X.class) on an UNPOOLED MyBatis DataSource; framework probes that unwrap DataSources to detect pool vendors; copying HikariCP-style unwrap code when switching to MyBatis' built-in UNPOOLED factory.
Common situations: Monitoring/diagnostic libraries probing the DataSource; attempts to reach driver-specific configuration through the DataSource; migrations between pool implementations.
Related errors
- ${className} is not a wrapper.
- There was an error configuring JndiDataSourceTransactionPool
- Unknown DataSource property: ${propertyName}
- Parameter 'dataSource' must not be null
- dataSource cannot be null
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/1f014f7e24fbb439.
Report an issue: GitHub.