{"id":2822,"date":"2014-05-16T14:44:33","date_gmt":"2014-05-16T12:44:33","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=2822"},"modified":"2014-05-16T14:44:33","modified_gmt":"2014-05-16T12:44:33","slug":"inselraus-jdbc-metadaten","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2014\/05\/inselraus-jdbc-metadaten\/","title":{"rendered":"Inselraus: JDBC Metadaten"},"content":{"rendered":"<p>Von einer Datenbank k\u00f6nnen verschiedene Informationen ausgelesen werden. Zum einen sind dies Informationen zu einer bestimmten Tabelle, zum anderen Informationen \u00fcber die Datenbank selbst.<\/p>\n<h3>Metadaten \u00fcber die Tabelle<\/h3>\n<p>Bei der Abfrage \u00fcber alle Spalten m\u00fcssen 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\u00f6nnen wir nicht auf die Daten zugreifen.<\/p>\n<p>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\u00f6nnen f\u00fcr jede Abfrage angefordert werden. So l\u00e4sst sich unter anderem leicht herausfinden:<\/p>\n<ul>\n<li>wie viele Spalten wir in einer Zeile abfragen k\u00f6nnen<\/li>\n<li>wie der Name der Spalte lautet<\/li>\n<li>welchen SQL-Typ die Spalte hat<\/li>\n<li>ob NULL f\u00fcr eine Spalte in Ordnung ist<\/li>\n<li>wie viele Dezimalzeichen eine Spalte hat<\/li>\n<\/ul>\n<h4>Einige Informationen \u00fcber die Bestellelemente<\/h4>\n<p>Um Anzahl und Art der Spalten einer Bestelltabelle herauszufinden, werden wir zun\u00e4chst ein ResultSet mit stmt.executeQuery(&#8222;SELECT * FROM Item&#8220;) erzeugen und dann via getMetaData() ein ResultSetMetaData-Objekt erfragen. Das ResultSetMetaData-Objekt besitzt viele Methoden, um Aussagen \u00fcber die Tabelle und die Spalten zu treffen. So fragen wir mit getColumnCount() nach, wie viele Spalten die Tabelle hat. Anschlie\u00dfend l\u00e4sst sich f\u00fcr jede Spalte der Name und Typ erfragen:<\/p>\n<pre>String url = \"jdbc:hsqldb:file:TutegoDB;shutdown=true\";<\/pre>\n<pre>String sql = \"SELECT * FROM ITEM\";<\/pre>\n<pre>try ( Connection con = DriverManager.getConnection( url, \"sa\", \"\" );<\/pre>\n<pre>\u00a0\u00a0\u00a0\u00a0\u00a0 Statement stmt = con.createStatement();<\/pre>\n<pre>\u00a0\u00a0\u00a0\u00a0\u00a0 ResultSet rs = con.createStatement().executeQuery( sql ) ) {<\/pre>\n<pre>\u00a0 ResultSetMetaData meta = rs.getMetaData();<\/pre>\n<pre><\/pre>\n<pre>\u00a0 int numerics = 0;<\/pre>\n<pre><\/pre>\n<pre>\u00a0 for ( int i = 1; i &lt;= meta.getColumnCount(); i++ ) {<\/pre>\n<pre>\u00a0\u00a0\u00a0 System.out.printf( \"%-20s %-20s%n\", meta.getColumnLabel( i ),<\/pre>\n<pre>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 meta.getColumnTypeName( i ) );<\/pre>\n<pre>\u00a0\u00a0\u00a0 if ( meta.isSigned( i ) )<\/pre>\n<pre>\u00a0\u00a0\u00a0\u00a0\u00a0 numerics++;<\/pre>\n<pre>\u00a0 }<\/pre>\n<pre><\/pre>\n<pre>\u00a0 System.out.println();<\/pre>\n<pre>\u00a0 System.out.println( \"Spalten: \" + meta.getColumnCount() +<\/pre>\n<pre>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \", Numerisch: \" + numerics );<\/pre>\n<pre>}<\/pre>\n<p>interface\u00a0java.sql.ResultSet<br \/>\nextends\u00a0Wrapper,\u00a0AutoCloseable<\/p>\n<ul>\n<li>ResultSetMetaDatagetMetaData()throwsSQLException<br \/>\nLiefert die Eigenschaften eines ResultSet in einem ResultSetMetaData zur\u00fcck.<\/li>\n<\/ul>\n<p>interface\u00a0java.sql.ResultSetMetaData<br \/>\nextends\u00a0Wrapper<\/p>\n<ul>\n<li>intgetColumnCount()<br \/>\nLiefert die Anzahl der Spalten im aktuellen ResultSet. Das ist praktisch f\u00fcr SQL-Anweisungen wie SELECT *.<\/li>\n<\/ul>\n<p>Allen folgenden Methoden wird ein int \u00fcbergeben, das die Spalte kennzeichnet:<\/p>\n<ul>\n<li>StringgetCatalogName(intcolumn)<br \/>\nGibt den String mit dem Katalognamen der Tabelle f\u00fcr die angegebene Spalte zur\u00fcck.<\/li>\n<li>StringgetColumnName(intcolumn)<br \/>\nLiefert den Spaltennamen der Tabelle.<\/li>\n<li>intgetColumnDisplaySize(intcolumn)<br \/>\nMaximale 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.<\/li>\n<li>StringgetColumnLabel(intcolumn)<br \/>\nGibt einen String zur\u00fcck, der den Titel der angegebenen Spalte enth\u00e4lt. Der Titel gibt an, welche \u00dcberschrift f\u00fcr die Spalte angezeigt werden soll. Einige Datenbanken erlauben die Unterscheidung zwischen Spaltennamen und Spaltentitel.<\/li>\n<li>intgetColumnType(intcolumn)<br \/>\nDer 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\u00e4hlen 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(\u2026) oder setObject(\u2026) erfolgt.<\/li>\n<li>StringgetColumnTypeName(intcolumn)<br \/>\nLiefert den Namen der Spalte, so wie sie die Datenbank definiert.<\/li>\n<li>intgetPrecision(intcolumn)<br \/>\nLiefert die Dezimalgenauigkeit der Spalte, zur\u00fcckgegeben als Anzahl der Ziffern.<\/li>\n<li>intgetScale(intcolumn)<br \/>\nLiefert die Genauigkeit der Spalte. Dies ist die Anzahl der Stellen, die nach dem Dezimalpunkt verwendet werden k\u00f6nnen.<\/li>\n<li>StringgetSchemaName(intcolumn)<br \/>\nDer Name des Tabellenschemas. Wird von den Methoden des DatabaseMetaData-Objekts benutzt. Falls kein Schema vorhanden ist, wird &#8222;&#8220; zur\u00fcckgegeben.<\/li>\n<li>StringgetTableName(intcolumn)<br \/>\nLiefert den Tabellennamen der angegebenen Spalte.<\/li>\n<li>booleanisAutoIncrement(intcolumn)<br \/>\nStellt fest, ob eine Spalte eine Auto-Increment-Spalte ist. Diese nimmt dann automatisch den n\u00e4chsten freien Wert an, wenn ein neuer Datensatz eingef\u00fcgt wird. Ist die erste Zeile einer Tabelle mit einer Auto-Increment-Spalte eingef\u00fcgt, so nimmt die Spalte den Wert 1 an. In den meisten Datenbanken ist es allerdings nicht m\u00f6glich, eigene Werte in diesen Spalten einzutragen.<\/li>\n<li>booleanisCaseSensitive(intcolumn)<br \/>\nBer\u00fccksichtigt die Spalte die Gro\u00df- bzw. Kleinschreibung?<\/li>\n<li>booleanisCurrency(intcolumn)<br \/>\nEnth\u00e4lt die Spalte Geldwerte? Nur einige Datenbanken bieten diesen Spaltentyp.<\/li>\n<li>booleanisNullable(intcolumn)<br \/>\nIst ein SQL-NULL in der Spalte erlaubt?<\/li>\n<li>booleanisSearchable(intcolumn)<br \/>\nKann die Spalte in einer SQL-WHERE-Klausel verwendet werden?<\/li>\n<li>booleanisSigned(intcolumn)<br \/>\nEnth\u00e4lt die Spalte vorzeichenbehaftete Datentypen? Vorzeichenbehaftete Typen sind unter anderem INT, LONGINT und SMALLINT. Vorzeichenlose Typen sind unter anderem UINT, ULONG und UBYTE.<\/li>\n<li>booleanisReadOnly(intcolumn)<br \/>\nIst es m\u00f6glich, auf die Spalte definitiv nicht schreibend zuzugreifen? Ist das Ergebnis true, kann der Wert also nicht aktualisiert werden.<\/li>\n<li>booleanisWritable(intcolumn)<br \/>\nIst es prinzipiell m\u00f6glich, auf die Spalte schreibend zuzugreifen? H\u00e4ufig wird das als !isReadOnly(column) implementiert.<\/li>\n<li>booleanisDefinitelyWritable(intcolumn)<br \/>\nKann auf die Spalte definitiv schreibend zugegriffen werden? Viele Datenbanken liefern die gleichen Ergebnisse bei isDefinitelyWritable(\u2026) und isWritable(\u2026). Prinzipiell k\u00f6nnte der Zustand von isWritable(\u2026) abweichen, wenn sich zum Beispiel die Schreibbarkeit dynamisch \u00e4ndert.<\/li>\n<\/ul>\n<p>Alle Methoden k\u00f6nnen eine SQLException ausl\u00f6sen.<\/p>\n<h3>Informationen \u00fcber die Datenbank<\/h3>\n<p>Metadaten sind auch f\u00fcr die gesamte Datenbank abfragbar. Beispiele f\u00fcr diese Informationen sind:<\/p>\n<ul>\n<li>Welche Tabellen liegen in der Datenbank?<\/li>\n<li>Wer ist mit der Datenbank verbunden?<\/li>\n<li>Kann die Datenbank nur gelesen oder kann auch in die Datenbank geschrieben werden?<\/li>\n<li>Wie lauten die Prim\u00e4rschl\u00fcssel f\u00fcr eine Tabelle?<\/li>\n<li>Sind gespeicherte Prozeduren auf der Datenbankseite erlaubt?<\/li>\n<li>Lassen sich \u00e4u\u00dfere Joins (outer joins) durchf\u00fchren?<\/li>\n<\/ul>\n<p>Sind Informationen \u00fcber die Datenbank gefragt, so lassen sich \u00fcber Metadaten eines DatabaseMetaData-Objekts beispielsweise Datenbankeigenschaften des Herstellers herausfinden. Zun\u00e4chst ben\u00f6tigen wir dazu ein DatabaseMetaData-Objekt, das uns getMetaData() von einer Connection gibt. Das DatabaseMetaData-Objekt deklariert eine gro\u00dfe Anzahl Methoden:<\/p>\n<pre>DatabaseMetaData\u00a0meta\u00a0=\u00a0con.getMetaData();\r\nSystem.out.println(\u00a0\"Product\u00a0name\u00a0\"\u00a0+\u00a0meta.getDatabaseProductName()\u00a0);\r\nSystem.out.println(\u00a0\"Version:\u00a0\"\u00a0+\u00a0meta.getDatabaseProductVersion()\u00a0\u00a0);\r\nSystem.out.println(\u00a0\"Maximum\u00a0number\u00a0of\u00a0connections:\u00a0\"\u00a0+\u00a0meta.getMaxConnections()\u00a0);\r\nSystem.out.println(\u00a0\"JDBC\u00a0driver\u00a0version:\u00a0\"\u00a0+\u00a0meta.getDriverVersion()\u00a0);\r\nSystem.out.println(\u00a0\"Supports\u00a0update\u00a0in\u00a0batch:\u00a0\"\u00a0+\u00a0meta.supportsBatchUpdates()\u00a0);\r\nSystem.out.println(\u00a0\"Supports\u00a0stored\u00a0procedures:\u00a0\"\u00a0+\u00a0meta.supportsStoredProcedures()\u00a0);<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Von einer Datenbank k\u00f6nnen verschiedene Informationen ausgelesen werden. Zum einen sind dies Informationen zu einer bestimmten Tabelle, zum anderen Informationen \u00fcber die Datenbank selbst. Metadaten \u00fcber die Tabelle Bei der Abfrage \u00fcber alle Spalten m\u00fcssen wir die Struktur der Datenbank kennen, insbesondere dann, wenn wir allgemeine Abfragen vornehmen und die passenden Daten herauslesen wollen. So [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[11],"tags":[],"class_list":["post-2822","post","type-post","status-publish","format-standard","hentry","category-insel"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2822","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=2822"}],"version-history":[{"count":1,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2822\/revisions"}],"predecessor-version":[{"id":2823,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2822\/revisions\/2823"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=2822"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=2822"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=2822"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}