W3docs

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:

MethodeGibt 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 auf getDatabaseProductName().

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.

java— editable, runs on the server

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 die Types-Klasse. Ein echter generischer Reader erstellt dieses Wörterbuch einmalig und verwendet es für jede Ergebnismenge wieder.
  • ResultSetMetaData.getColumnType(i) gibt einen dieser int-Codes zurück; das Wörterbuch wandelt Code 4 in INTEGER, 12 in VARCHAR, 93 in TIMESTAMP um. 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/getColumnType für eine echte Abfrage liefern. Er ist die Grundlage von Grid-Viewern, CSV-Exportern und ORMs, die Spalten auf Felder abbilden.
  • DatabaseMetaData beantwortet 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 Tabellenkata­log.
  • 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.
Hinweis
Die Anzahl der 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 TransactionssupportsTransactions() und Savepoint-Unterstützung werden von DatabaseMetaData gemeldet.

Übungen

Übung
Sie schreiben ein generisches Werkzeug, das die Spaltennamen und -typen einer beliebigen SQL-Abfrage ausgeben soll, ohne vorherige Kenntnis des Schemas. Welches Interface liefert diese Informationen?
Sie schreiben ein generisches Werkzeug, das die Spaltennamen und -typen einer beliebigen SQL-Abfrage ausgeben soll, ohne vorherige Kenntnis des Schemas. Welches Interface liefert diese Informationen?
Was this page helpful?