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

  1. Call isWrapperFor(iface) first and skip unwrap when it returns false
  2. Unwrap the java.sql.Connection from getConnection() (or its DriverProxy) for driver-specific access instead of the DataSource
  3. 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

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


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