mybatis/mybatis-3 · warning · SQLException

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.

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.

Appendix: 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)