Java JDBC Metadata
Datenbanken und Ergebnismengen zur Laufzeit in Java mit DatabaseMetaData und ResultSetMetaData untersuchen.
Metadata sind Daten über die Daten — die Struktur einer Ergebnismenge und die Fähigkeiten der Datenbank, nicht die Zeilen selbst. JDBC stellt zwei Metadata-Interfaces bereit, die unterschiedliche Fragen beantworten. Sie sind die Grundlage generischer Werkzeuge: Zeilendruckern, Schema-Browsern, ORMs und Migrationsskripten, die ohne fest kodiertes Wissen über die Tabellen funktionieren müssen.
Dieses Kapitel behandelt beide Interfaces — ResultSetMetaData (die Spalten, die eine Abfrage zurückgegeben hat) und DatabaseMetaData (die Datenbank und der Treiber selbst) — wie SQL-Typecodes gelesen werden, und ein ausführbares Beispiel, das ein Typecode-Wörterbuch aufbaut. Es setzt voraus, dass Sie bereits wissen, wie man eine JDBC-Verbindung öffnet und ein ResultSet iteriert.
ResultSetMetaData — Spalten einer Abfrage beschreiben
Ein ResultSet enthält die Zeilen; sein ResultSetMetaData enthält die Spaltenbeschreibungen. Rufen Sie rs.getMetaData() auf, um zu erfahren, welche Spalten eine Abfrage zurückgegeben hat: wie viele, ihre Namen und ihre SQL-Typen. So rendert ein generisches Grid jede beliebige Abfrage:
ResultSetMetaData md = rs.getMetaData();
int n = md.getColumnCount();
for (int i = 1; i <= n; i++) { // 1-based, of course
System.out.println(md.getColumnLabel(i)
+ " : " + md.getColumnTypeName(i)
+ " (java.sql.Types " + md.getColumnType(i) + ")");
}Spaltenindizes sind 1-basiert, nicht 0-basiert — getColumnLabel(0) wirft eine Ausnahme. Die nützlichsten Methoden:
| Methode | Gibt zurück |
|---|---|
getColumnCount() | Anzahl der Spalten im Ergebnis |
getColumnLabel(i) | Anzeigename, unter Berücksichtigung von AS-Aliasnamen |
getColumnName(i) | Zugrunde liegender Spaltenname, ohne Aliasnamen |
getColumnType(i) | Der java.sql.Types int-Code (z. B. 4 für INTEGER) |
getColumnTypeName(i) | Der typspezifische Name des Herstellers (z. B. INT4 auf PostgreSQL) |
isNullable(i) | columnNoNulls, columnNullable oder columnNullableUnknown |
getPrecision(i) / getScale(i) | Größe und Dezimalstellen, nützlich für NUMERIC-Spalten |
Bevorzugen Sie getColumnLabel gegenüber getColumnName, wenn Sie Überschriften anzeigen: Es respektiert SELECT total AS revenue, sodass die Überschrift revenue lautet.
DatabaseMetaData — die Datenbank beschreiben
Rufen Sie conn.getMetaData() auf, um Informationen über die Datenbank und den Treiber zu erhalten: Produktname und -version, unterstützte Funktionen sowie den Katalog der Tabellen, Spalten, Schlüssel und Indizes.
DatabaseMetaData dbmd = conn.getMetaData();
System.out.println(dbmd.getDatabaseProductName() + " " + dbmd.getDatabaseProductVersion());
System.out.println("supports transactions: " + dbmd.supportsTransactions());
// the schema catalog comes back as ResultSets you read normally
try (ResultSet tables = dbmd.getTables(null, null, "%", new String[]{"TABLE"})) {
while (tables.next()) System.out.println(tables.getString("TABLE_NAME"));
}Beachten Sie, dass getTables, getColumns und ähnliche Methoden ResultSets zurückgeben — der Katalog wird mit derselben Cursor-API wie andere Daten abgefragt. Jede Zeile von getColumns hat bekannte Spaltenbeschriftungen, die Sie nach Name lesen:
// columns of the "users" table, in any catalog/schema
try (ResultSet cols = dbmd.getColumns(null, null, "users", "%")) {
while (cols.next()) {
System.out.println(cols.getString("COLUMN_NAME")
+ " " + cols.getString("TYPE_NAME")
+ (cols.getInt("NULLABLE") == DatabaseMetaData.columnNoNulls ? " NOT NULL" : ""));
}
}Die vier Argumente für getTables/getColumns sind catalog, schemaPattern, ein Namensmuster und (für getTables) die Tabellentypen. null bedeutet „nicht filtern", und % ist der SQL-Platzhalter für „beliebiger Name". Häufige Begleitmethoden sind getPrimaryKeys, getImportedKeys (Fremdschlüssel) und getIndexInfo.
Wann welches Interface verwenden
- Verwenden Sie
ResultSetMetaData, wenn Sie ein Abfrageergebnis haben und dessen Spalten beschreiben müssen — ein Grid-Viewer, ein CSV-Exporter, ein Object-Mapper. - Verwenden Sie
DatabaseMetaData, wenn Sie vor der Abfrage Informationen über die Datenbank benötigen — Funktionserkennung (unterstützt sie Batch-Updates? Savepoints?), Schema-Erkundung für ein Migrationswerkzeug oder SQL-Verzweigung basierend aufgetDatabaseProductName().
Typecodes auf Namen abbilden
getColumnType(i) gibt einen int zurück — eine der java.sql.Types-Konstanten wie INTEGER (4), VARCHAR (12) oder TIMESTAMP (93). Der rohe int ist umständlich zu lesen und zu verarbeiten, daher erstellt ein generischer Reader einmalig ein Code-zu-Name-Wörterbuch (durch Reflection über java.sql.Types) und verwendet es für jede Ergebnismenge wieder. Derselbe Code zeigt auch, welchen typisierten Getter man aufrufen soll — getInt, getString, getTimestamp — wenn man Spalten auf Felder abbildet, so wie es ein PreparedStatement-gesteuerter Mapper tut.
Ein Beispiel: ein Typecode-Wörterbuch und ein Spaltenbericht
Dieses Programm erstellt das int→Name-Wörterbuch für alle java.sql.Types-Konstanten, verwendet es dann zur Beschreibung eines hypothetischen dreiSpaltigen Ergebnisses — so wie ResultSetMetaData es tun würde — und listet die Arten von Fragen auf, die DatabaseMetaData beantwortet — ohne eine aktive Datenbankverbindung.
Was aus dem Lauf zu lernen ist:
- Es gibt 39 Standard-
java.sql.Types-Codes, und das Programm erstellt die gesamte Code→Name-Zuordnung durch Reflection über dieTypes-Klasse. Ein echter generischer Reader erstellt dieses Wörterbuch einmalig und verwendet es für jede Ergebnismenge wieder. ResultSetMetaData.getColumnType(i)gibt einen dieserint-Codes zurück; das Wörterbuch wandelt Code4inINTEGER,12inVARCHAR,93inTIMESTAMPum. Diese Nachschlageoperation ermöglicht es einem Werkzeug, jede beliebige Abfrage zu rendern, ohne deren Spalten im Voraus zu kennen.- Der Spaltenbericht — Name plus Typ — ist das, was
getColumnLabel/getColumnTypefür eine echte Abfrage liefern. Er ist die Grundlage von Grid-Viewern, CSV-Exportern und ORMs, die Spalten auf Felder abbilden. DatabaseMetaDatabeantwortet eine andere Klasse von Fragen: nicht „was hat diese Abfrage zurückgegeben", sondern „was kann diese Datenbank leisten" — ihren Produktnamen, die Treiberversion, die Funktionsunterstützung und den Tabellenkatalog.- Entscheidend ist, dass die Katalogmethoden (
getTables,getColumns)ResultSets zurückgeben, sodass Sie die Datenbankstruktur mit derselben Cursor-Schleife lesen, die Sie für Daten verwenden. Metadata ist keine spezielle API — es sind Daten über Daten, auf die gleiche Weise geliefert.
java.sql.Types-Codes (hier 39) kann zwischen JDK-Versionen leicht variieren, wenn neue Konstanten hinzugefügt werden. Erstellen Sie das Wörterbuch wie oben gezeigt durch Reflection, anstatt die Anzahl fest zu kodieren.Wie es weitergeht
- JDBC ResultSet — die Cursor-API, die sowohl
getMetaData()als auch die Katalogmethoden zurückgeben. - JDBC Connection — woher
getMetaData()auf der Verbindung stammt. - JDBC PreparedStatement — metadata-gesteuerte Spaltenzuordnung mit sicherer Parameterbindung kombinieren.
- JDBC Transactions —
supportsTransactions()und Savepoint-Unterstützung werden vonDatabaseMetaDatagemeldet.