Inselraus: JDBC Metadaten

Von einer Datenbank können verschiedene Informationen ausgelesen werden. Zum einen sind dies Informationen zu einer bestimmten Tabelle, zum anderen Informationen über die Datenbank selbst.

Metadaten über die Tabelle

Bei der Abfrage über alle Spalten müssen wir die Struktur der Datenbank kennen, insbesondere dann, wenn wir allgemeine Abfragen vornehmen und die passenden Daten herauslesen wollen. So liefert SELECT * FROM Item ein ResultSet mit der Anzahl der Spalten, wie sie die Tabelle Item hat. Doch bevor wir nicht die Anzahl und die Art der Spalten kennen, können wir nicht auf die Daten zugreifen.

Um diese Art von Informationen, so genannte Metadaten, in Erfahrung zu bringen, befindet sich die Klasse ResultSetMetaData, mit der wir diese Informationen erhalten, unter den SQL-Klassen. Metadaten können für jede Abfrage angefordert werden. So lässt sich unter anderem leicht herausfinden:

  • wie viele Spalten wir in einer Zeile abfragen können
  • wie der Name der Spalte lautet
  • welchen SQL-Typ die Spalte hat
  • ob NULL für eine Spalte in Ordnung ist
  • wie viele Dezimalzeichen eine Spalte hat

Einige Informationen über die Bestellelemente

Um Anzahl und Art der Spalten einer Bestelltabelle herauszufinden, werden wir zunächst ein ResultSet mit stmt.executeQuery(„SELECT * FROM Item“) erzeugen und dann via getMetaData() ein ResultSetMetaData-Objekt erfragen. Das ResultSetMetaData-Objekt besitzt viele Methoden, um Aussagen über die Tabelle und die Spalten zu treffen. So fragen wir mit getColumnCount() nach, wie viele Spalten die Tabelle hat. Anschließend lässt sich für jede Spalte der Name und Typ erfragen:

String url = "jdbc:hsqldb:file:TutegoDB;shutdown=true";
String sql = "SELECT * FROM ITEM";
try ( Connection con = DriverManager.getConnection( url, "sa", "" );
      Statement stmt = con.createStatement();
      ResultSet rs = con.createStatement().executeQuery( sql ) ) {
  ResultSetMetaData meta = rs.getMetaData();

  int numerics = 0;

  for ( int i = 1; i <= meta.getColumnCount(); i++ ) {
    System.out.printf( "%-20s %-20s%n", meta.getColumnLabel( i ),
                                        meta.getColumnTypeName( i ) );
    if ( meta.isSigned( i ) )
      numerics++;
  }

  System.out.println();
  System.out.println( "Spalten: " + meta.getColumnCount() +
                      ", Numerisch: " + numerics );
}

interface java.sql.ResultSet
extends Wrapper, AutoCloseable

  • ResultSetMetaDatagetMetaData()throwsSQLException
    Liefert die Eigenschaften eines ResultSet in einem ResultSetMetaData zurück.

interface java.sql.ResultSetMetaData
extends Wrapper

  • intgetColumnCount()
    Liefert die Anzahl der Spalten im aktuellen ResultSet. Das ist praktisch für SQL-Anweisungen wie SELECT *.

Allen folgenden Methoden wird ein int übergeben, das die Spalte kennzeichnet:

  • StringgetCatalogName(intcolumn)
    Gibt den String mit dem Katalognamen der Tabelle für die angegebene Spalte zurück.
  • StringgetColumnName(intcolumn)
    Liefert den Spaltennamen der Tabelle.
  • intgetColumnDisplaySize(intcolumn)
    Maximale Anzahl der Zeichen, die die Spalte einnimmt. So ist bei einer Spalte vom Typ VARCHAR(11) mit einer maximalen Spaltenbreite von zehn Zeichen zu rechnen. Bei numerischen Spalten variiert der Wert.
  • StringgetColumnLabel(intcolumn)
    Gibt einen String zurück, der den Titel der angegebenen Spalte enthält. Der Titel gibt an, welche Überschrift für die Spalte angezeigt werden soll. Einige Datenbanken erlauben die Unterscheidung zwischen Spaltennamen und Spaltentitel.
  • intgetColumnType(intcolumn)
    Der Typ der Spalte wird ermittelt. Der Spaltentyp ist dabei eine Konstante aus der Klasse java.sql.Types. Sie deklariert Konstanten nach dem XOPEN-Standard. Dazu zählen unter anderem BIGINT, BINARY, BIT, BLOB_LOCATOR, CHAR, CLOB_LOCATOR, DATE, DECIMAL, DISTINCT, DOUBLE, FLOAT, INTEGER, JAVA_OBJECT (benutzerdefinierter Datentyp), STRUCT, TIME, TIMESTAMP, TINYINT, VARBINARY, VARCHAR. Die Konstante OTHER zeigt ein datenbankspezifisches Element an und wird auf ein Java-Objekt abgebildet, falls ein Zugriff mittels getObject(…) oder setObject(…) erfolgt.
  • StringgetColumnTypeName(intcolumn)
    Liefert den Namen der Spalte, so wie sie die Datenbank definiert.
  • intgetPrecision(intcolumn)
    Liefert die Dezimalgenauigkeit der Spalte, zurückgegeben als Anzahl der Ziffern.
  • intgetScale(intcolumn)
    Liefert die Genauigkeit der Spalte. Dies ist die Anzahl der Stellen, die nach dem Dezimalpunkt verwendet werden können.
  • StringgetSchemaName(intcolumn)
    Der Name des Tabellenschemas. Wird von den Methoden des DatabaseMetaData-Objekts benutzt. Falls kein Schema vorhanden ist, wird „“ zurückgegeben.
  • StringgetTableName(intcolumn)
    Liefert den Tabellennamen der angegebenen Spalte.
  • booleanisAutoIncrement(intcolumn)
    Stellt fest, ob eine Spalte eine Auto-Increment-Spalte ist. Diese nimmt dann automatisch den nächsten freien Wert an, wenn ein neuer Datensatz eingefügt wird. Ist die erste Zeile einer Tabelle mit einer Auto-Increment-Spalte eingefügt, so nimmt die Spalte den Wert 1 an. In den meisten Datenbanken ist es allerdings nicht möglich, eigene Werte in diesen Spalten einzutragen.
  • booleanisCaseSensitive(intcolumn)
    Berücksichtigt die Spalte die Groß- bzw. Kleinschreibung?
  • booleanisCurrency(intcolumn)
    Enthält die Spalte Geldwerte? Nur einige Datenbanken bieten diesen Spaltentyp.
  • booleanisNullable(intcolumn)
    Ist ein SQL-NULL in der Spalte erlaubt?
  • booleanisSearchable(intcolumn)
    Kann die Spalte in einer SQL-WHERE-Klausel verwendet werden?
  • booleanisSigned(intcolumn)
    Enthält die Spalte vorzeichenbehaftete Datentypen? Vorzeichenbehaftete Typen sind unter anderem INT, LONGINT und SMALLINT. Vorzeichenlose Typen sind unter anderem UINT, ULONG und UBYTE.
  • booleanisReadOnly(intcolumn)
    Ist es möglich, auf die Spalte definitiv nicht schreibend zuzugreifen? Ist das Ergebnis true, kann der Wert also nicht aktualisiert werden.
  • booleanisWritable(intcolumn)
    Ist es prinzipiell möglich, auf die Spalte schreibend zuzugreifen? Häufig wird das als !isReadOnly(column) implementiert.
  • booleanisDefinitelyWritable(intcolumn)
    Kann auf die Spalte definitiv schreibend zugegriffen werden? Viele Datenbanken liefern die gleichen Ergebnisse bei isDefinitelyWritable(…) und isWritable(…). Prinzipiell könnte der Zustand von isWritable(…) abweichen, wenn sich zum Beispiel die Schreibbarkeit dynamisch ändert.

Alle Methoden können eine SQLException auslösen.

Informationen über die Datenbank

Metadaten sind auch für die gesamte Datenbank abfragbar. Beispiele für diese Informationen sind:

  • Welche Tabellen liegen in der Datenbank?
  • Wer ist mit der Datenbank verbunden?
  • Kann die Datenbank nur gelesen oder kann auch in die Datenbank geschrieben werden?
  • Wie lauten die Primärschlüssel für eine Tabelle?
  • Sind gespeicherte Prozeduren auf der Datenbankseite erlaubt?
  • Lassen sich äußere Joins (outer joins) durchführen?

Sind Informationen über die Datenbank gefragt, so lassen sich über Metadaten eines DatabaseMetaData-Objekts beispielsweise Datenbankeigenschaften des Herstellers herausfinden. Zunächst benötigen wir dazu ein DatabaseMetaData-Objekt, das uns getMetaData() von einer Connection gibt. Das DatabaseMetaData-Objekt deklariert eine große Anzahl Methoden:

DatabaseMetaData meta = con.getMetaData();
System.out.println( "Product name " + meta.getDatabaseProductName() );
System.out.println( "Version: " + meta.getDatabaseProductVersion()  );
System.out.println( "Maximum number of connections: " + meta.getMaxConnections() );
System.out.println( "JDBC driver version: " + meta.getDriverVersion() );
System.out.println( "Supports update in batch: " + meta.supportsBatchUpdates() );
System.out.println( "Supports stored procedures: " + meta.supportsStoredProcedures() );

Über Christian Ullenboom

Ich bin Christian Ullenboom und Autor der Bücher ›Java ist auch eine Insel. Einführung, Ausbildung, Praxis‹ und ›Java SE 8 Standard-Bibliothek. Das Handbuch für Java-Entwickler‹. Seit 1997 berate ich Unternehmen im Einsatz von Java. Sun ernannte mich 2005 zum ›Java-Champion‹.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.