{"record":{"id":"1f014f7e24fbb439","repo":"mybatis/mybatis-3","slug":"classname-is-not-a-wrapper-1f014f","errorCode":null,"errorMessage":"${className} is not a wrapper.","messagePattern":"(.+?) is not a wrapper\\.","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"warning","filePath":"src/main/java/org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java","lineNumber":312,"sourceCode":"    @Override\n    public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {\n      return this.driver.getPropertyInfo(u, p);\n    }\n\n    @Override\n    public boolean jdbcCompliant() {\n      return this.driver.jdbcCompliant();\n    }\n\n    @Override\n    public Logger getParentLogger() {\n      return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);\n    }\n  }\n\n  @Override\n  public <T> T unwrap(Class<T> iface) throws SQLException {\n    throw new SQLException(getClass().getName() + \" is not a wrapper.\");\n  }\n\n  @Override\n  public boolean isWrapperFor(Class<?> iface) throws SQLException {\n    return false;\n  }\n\n  @Override\n  public Logger getParentLogger() {\n    // requires JDK version 1.6\n    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);\n  }\n\n}\n","sourceCodeStart":294,"sourceCodeEnd":327,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/datasource/unpooled/UnpooledDataSource.java#L294-L327","documentation":"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.","triggerScenarios":"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.","commonSituations":"Monitoring/diagnostic libraries probing the DataSource; attempts to reach driver-specific configuration through the DataSource; migrations between pool implementations.","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"],"exampleFix":"// before\nOracleDataSource ods = ds.unwrap(OracleDataSource.class); // throws\n\n// after\nif (ds.isWrapperFor(OracleDataSource.class)) {\n  OracleDataSource ods = ds.unwrap(OracleDataSource.class);\n}","handlingStrategy":"type-guard","validationCode":"if (ds.isWrapperFor(targetType)) {\n  T t = ds.unwrap(targetType);\n} else {\n  // UNPOOLED DataSource wraps nothing — use instanceof on concrete type instead\n}","typeGuard":"static <T> Optional<T> unwrapIfPossible(javax.sql.DataSource ds, Class<T> type) {\n  try {\n    return ds.isWrapperFor(type) ? Optional.of(ds.unwrap(type)) : Optional.empty();\n  } catch (SQLException e) {\n    return Optional.empty();\n  }\n}","tryCatchPattern":"try {\n  return ds.unwrap(type);\n} catch (SQLException e) {\n  if (e.getMessage().endsWith(\"is not a wrapper.\")) return Optional.empty();\n  throw e;\n}","preventionTips":["Guard every unwrap with isWrapperFor","Detect concrete pool types with instanceof (HikariDataSource etc.) rather than unwrap on MyBatis built-ins"],"tags":["mybatis","jdbc-wrapper","datasource","api-misuse"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}