mybatis/mybatis-3 · warning · SQLException

${className} is not a wrapper.

Error message

${className} is not a wrapper.

What it means

UnpooledDataSource and PooledDataSource implement the java.sql.Wrapper interface but do not wrap anything, so unwrap(Class) always throws SQLException '<ClassName> is not a wrapper.' and isWrapperFor returns false. Wrapper.unwrap is meant to let callers extract a vendor-specific implementation from a proxy; these DataSource implementations are plain, so there is nothing to unwrap.

Source

Thrown at src/main/java/org/apache/ibatis/datasource/pooled/PooledDataSource.java:631

  public static Connection unwrapConnection(Connection conn) {
    if (Proxy.isProxyClass(conn.getClass())) {
      InvocationHandler handler = Proxy.getInvocationHandler(conn);
      if (handler instanceof PooledConnection) {
        return ((PooledConnection) handler).getRealConnection();
      }
    }
    return conn;
  }

  @Override
  protected void finalize() throws Throwable {
    forceCloseAll();
    super.finalize();
  }

  @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) {
    return false;
  }

  @Override
  public Logger getParentLogger() {
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
  }

}

View on GitHub (pinned to 008069adb1)

Solutions

  1. Guard with isWrapperFor(iface) before calling unwrap() — it returns false for these classes
  2. To get vendor/driver-specific objects, unwrap the java.sql.Connection obtained from getConnection(), not the DataSource
  3. If you need a pool with unwrap support (JMX metrics, etc.), use an external pool (HikariCP, DBCP) and plug it into MyBatis via a custom DataSourceFactory or env-provided DataSource

Example fix

// before
HikariDataSource h = dataSource.unwrap(HikariDataSource.class); // SQLException

// after
if (dataSource.isWrapperFor(HikariDataSource.class)) {
  HikariDataSource h = dataSource.unwrap(HikariDataSource.class);
} else {
  // MyBatis POOLED/UNPOOLED DataSource: nothing to unwrap
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (dataSource.isWrapperFor(HikariDataSource.class)) { // false for MyBatis pools
  HikariDataSource h = dataSource.unwrap(HikariDataSource.class);
}

Type guard

static boolean isUnwrappable(javax.sql.DataSource ds, Class<?> iface) {
  return ds != null && ds.isWrapperFor(iface); // MyBatis Pooled/Unpooled return false
}

Try / catch

try {
  return dataSource.unwrap(iface);
} catch (SQLException e) {
  if (e.getMessage().endsWith("is not a wrapper.")) return null; // not a proxy
  throw e;
}

Prevention

When it happens

Trigger: Calling pooledDataSource.unwrap(SomeClass.class) on the MyBatis DataSource itself; generic framework code (monitoring, metrics, de-correlators) that probes DataSources via unwrap(); attempting unwrap(Connection.class) or a driver class against the DataSource rather than against a Connection obtained from it.

Common situations: Integration libraries that try to detect HikariCP/Tomcat/uwm-specific datasources by unwrapping them; migration from a wrapping pool (HikariCP) to MyBatis PooledDataSource where code unwrapped HikariDataSource; trying to access driver-level APIs through the DataSource.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/559c9734a8335926. Report an issue: GitHub.