IMetadataHandler
Overview
IMetadataHandler
(org.dbunit.database, since 2.4.4) controls how a connection queries
java.sql.DatabaseMetaData for table, column, and primary-key metadata. dbUnit’s
own table-discovery logic (DatabaseDataSet) never calls DatabaseMetaData
directly — it always goes through the configured handler, which is what lets a
handler correct a vendor driver’s metadata quirks without touching core code.
Register one via
DatabaseConfig.PROPERTY_METADATA_HANDLER;
unset, DefaultMetadataHandler applies.
Interface Shape
Every method takes the real DatabaseMetaData (or a ResultSet it already produced)
plus the schema/table/column dbUnit is searching for, and returns either a ResultSet
to iterate or a boolean/String answer. Full signatures are in the
JavaDoc; grouped by
purpose:
| Group | Methods |
|---|---|
| Metadata lookups | getTables(), getColumns(), getPrimaryKeys(), tableExists() —
run the actual DatabaseMetaData query. This is the seam a handler overrides to change
which rows come back — see H2MetadataHandler/MultiSchemaMySqlMetadataHandler below. |
| Row interpretation | getSchema(ResultSet) extracts the schema name from a
getTables() row — some vendors (MySQL) report it in the catalog column instead. |
| Matching | matches(…) (two overloads) compares a getColumns() row against a
searched catalog/schema/table/column. This is the seam a handler overrides to fix
catalog/schema mismatches — see MySQL’s NoSuchColumnException fix in
MySQL. matchesColumn(…) (since 3.2.1) is a value-based
counterpart so a caller that already extracted and cached a row’s values can replay the
same comparison without re-querying or holding a ResultSet open;
supportsColumnCache() opts a handler into that fast path — return true only if
matchesColumn(…) fully replicates the handler’s matches(…) override. |
Built-in Implementations
| Handler | Use it when |
|---|---|
DefaultMetadataHandler |
No vendor-specific quirk to correct for — applied
automatically when PROPERTY_METADATA_HANDLER is left unset. |
Db2MetadataHandler |
DB2, fixing a catalog/schema column-matching bug. |
MySqlMetadataHandler |
MySQL/MariaDB, fixing catalog/schema comparison so qualified-table-name lookups don’t spuriously miss. |
MultiSchemaMySqlMetadataHandler |
A MySQL connection
(e.g. as root) that must see tables across every catalog it can access, not just its
current one. |
NetezzaMetadataHandler |
Netezza, which reports schema information via the catalog column. |
H2MetadataHandler |
H2 2.x, excluding INFORMATION_SCHEMA
from unscoped table listings — see below. |
InMemoryMetadataResultSet
A java.sql.ResultSet is a forward-only cursor over one query — rows can’t be filtered
out of it, or several combined, after the fact. That’s a problem for a handler that
needs to correct which rows come back, not just how they’re compared:
- MySQL Connector/J’s
nullCatalogMeansCurrentdefault treats anullcatalog as "the connection’s current catalog only" instead of "every catalog," per the JDBC spec. Correcting that means issuing one realgetTables()call per catalog and combining the results. - H2 2.x’s
INFORMATION_SCHEMAnow reports its own internal tables with the same JDBCTABLE_TYPEas real user tables (see H2). Correcting that means running the realgetTables()call once and dropping the rows that don’t belong.
InMemoryMetadataResultSet
(org.dbunit.database, since 3.5.0) is the shared building block behind both: it copies
the rows a handler wants to keep into memory ahead of time, then hands back a
java.lang.reflect.Proxy-based ResultSet that answers just the handful of methods
dbUnit itself calls against a metadata result — next(), getString(int/String),
getInt(int/String), getMetaData()/getColumnCount(), close() — throwing
UnsupportedOperationException on anything else, deliberately, rather than silently
returning a wrong answer to a method it was never taught.
| Factory | Use it to |
|---|---|
merge(List<ResultSet> sources) |
Concatenate rows from several real result sets into
one, in order. MultiSchemaMySqlMetadataHandler uses this to union one getTables()
call per visible catalog. |
filter(ResultSet source, RowFilter filter) |
Copy only the rows of one real result set
that pass a predicate. H2MetadataHandler uses this to drop INFORMATION_SCHEMA rows:
+
[source,java]
----
@Override
public ResultSet getTables(DatabaseMetaData metaData, String schemaName,
String[] tableType) throws SQLException
{
ResultSet resultSet = super.getTables(metaData, schemaName, tableType);
return InMemoryMetadataResultSet.filter(resultSet,
row → !"INFORMATION_SCHEMA".equalsIgnoreCase(getSchema(row)));
}
---- |
Both factories close every source result set they read, so a handler using them does not
need its own finally/close handling.
Writing a Custom Handler
Reach for a custom IMetadataHandler when a vendor’s JDBC driver misreports metadata in
a way that makes real tables/columns invisible or wrongly matched — not for filtering
data you don’t want in a dataset (that’s Filters) or mapping SQL
types (that’s Data Types).
Extend DefaultMetadataHandler and override only the method(s) whose default behavior
is wrong for the driver in question, then register the instance:
config.setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new MyVendorMetadataHandler());If the fix needs to change which rows a real query returns — dropping some, or
combining several real queries into one — use InMemoryMetadataResultSet as shown
above rather than hand-rolling a ResultSet implementation.


