Java JDBC Metadata
Ispeziona database e result set in fase di esecuzione in Java con DatabaseMetaData e ResultSetMetaData.
I metadati sono dati sui dati — la struttura di un result set e le capacità del database, anziché le righe stesse. JDBC espone due interfacce per i metadati, e rispondono a domande diverse. Alimentano strumenti generici: visualizzatori di righe, browser di schemi, ORM e script di migrazione che devono funzionare senza una conoscenza codificata delle tabelle.
Questo capitolo tratta entrambe le interfacce — ResultSetMetaData (le colonne restituite da una query) e DatabaseMetaData (il database e il driver stessi) — come leggere i codici di tipo SQL e un esempio eseguibile che costruisce un dizionario di codici di tipo. Si presuppone che tu sappia già come aprire una connessione JDBC e iterare un ResultSet.
ResultSetMetaData — descrivere le colonne di una query
Un ResultSet contiene le righe; il suo ResultSetMetaData contiene le descrizioni delle colonne. Chiama rs.getMetaData() per scoprire quali colonne ha restituito una query: quante sono, i loro nomi e i loro tipi SQL. Ecco come un grid generico visualizza qualsiasi query:
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) + ")");
}Gli indici di colonna sono in base 1, non 0 — getColumnLabel(0) lancia un'eccezione. I metodi più utili:
| Metodo | Restituisce |
|---|---|
getColumnCount() | numero di colonne nel risultato |
getColumnLabel(i) | nome visualizzato, rispettando qualsiasi alias AS |
getColumnName(i) | nome della colonna sottostante, ignorando gli alias |
getColumnType(i) | il codice int di java.sql.Types (es. 4 per INTEGER) |
getColumnTypeName(i) | il nome del tipo del vendor (es. INT4 su PostgreSQL) |
isNullable(i) | columnNoNulls, columnNullable, o columnNullableUnknown |
getPrecision(i) / getScale(i) | dimensione e cifre decimali, utile per le colonne NUMERIC |
Preferisci getColumnLabel a getColumnName quando mostri intestazioni: rispetta SELECT total AS revenue, quindi l'intestazione mostra revenue.
DatabaseMetaData — descrivere il database
Chiama conn.getMetaData() per ottenere informazioni sul database e sul driver stessi: nome e versione del prodotto, funzionalità supportate e il catalogo di tabelle, colonne, chiavi e indici.
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"));
}Nota che getTables, getColumns e metodi simili restituiscono ResultSets — il catalogo viene interrogato con la stessa API cursor degli altri dati. Ogni riga di getColumns ha etichette di colonna ben note che puoi leggere per nome:
// 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" : ""));
}
}I quattro argomenti di getTables/getColumns sono catalog, schemaPattern, un pattern del nome e (per getTables) i tipi di tabella. null significa "non filtrare", e % è il carattere jolly SQL per "qualsiasi nome". I metodi più comuni sono getPrimaryKeys, getImportedKeys (chiavi esterne) e getIndexInfo.
Quando usare quale interfaccia
- Usa
ResultSetMetaDataquando hai un risultato di query e devi descrivere le sue colonne — un visualizzatore a griglia, un esportatore CSV, un mappatore di oggetti. - Usa
DatabaseMetaDataquando hai bisogno di informazioni sul database prima di interrogarlo — rilevamento delle funzionalità (supporta gli aggiornamenti in batch? i savepoint?), scoperta dello schema per uno strumento di migrazione, o ramificazione SQL tramitegetDatabaseProductName().
Mappare i codici di tipo ai nomi
getColumnType(i) restituisce un int — una delle costanti di java.sql.Types come INTEGER (4), VARCHAR (12) o TIMESTAMP (93). L'int grezzo è scomodo da leggere e su cui fare switch, quindi un lettore generico costruisce una volta un dizionario codice-nome (riflettendo su java.sql.Types) e lo riutilizza per ogni result set. Lo stesso codice indica anche quale getter tipizzato chiamare — getInt, getString, getTimestamp — quando mappi le colonne ai campi, come fa un mappatore basato su PreparedStatement.
Un esempio pratico: un dizionario di codici di tipo e un report delle colonne
Questo programma costruisce il dizionario int→nome per ogni costante di java.sql.Types, poi lo usa per descrivere un ipotetico risultato a tre colonne come farebbe ResultSetMetaData — e elenca i tipi di domande a cui risponde DatabaseMetaData — senza un database attivo.
Cosa ricavare dall'esecuzione:
- Ci sono 39 codici
java.sql.Typesstandard, e il programma costruisce l'intera mappa codice→nome riflettendo sulla classeTypes. Un lettore generico reale costruisce questo dizionario una volta e lo riutilizza per ogni result set. ResultSetMetaData.getColumnType(i)restituisce uno di questi codiciint; il dizionario trasforma il codice4inINTEGER,12inVARCHAR,93inTIMESTAMP. Questa ricerca è esattamente ciò che consente a uno strumento di visualizzare qualsiasi query senza conoscerne le colonne in anticipo.- Il report per colonna — nome più tipo — è ciò che
getColumnLabel/getColumnTypeforniscono per una query reale. È la base dei visualizzatori a griglia, degli esportatori CSV e degli ORM che mappano le colonne ai campi. DatabaseMetaDatarisponde a una classe diversa di domande: non "cosa ha restituito questa query" ma "cosa può fare questo database" — il nome del prodotto, la versione del driver, il supporto delle funzionalità e il catalogo delle tabelle.- In modo cruciale, i metodi del catalogo (
getTables,getColumns) restituisconoResultSets, quindi si legge la struttura del database con lo stesso loop cursor usato per i dati. I metadati non sono un'API speciale — sono dati sui dati, consegnati nello stesso modo.
java.sql.Types (39 qui) può variare leggermente tra le versioni JDK man mano che vengono aggiunte nuove costanti. Costruisci il dizionario tramite riflessione, come sopra, anziché codificare il conteggio.Dove andare dopo
- JDBC ResultSet — l'API cursor restituita sia da
getMetaData()che dai metodi del catalogo. - JDBC Connection — dove
getMetaData()sulla connessione ha origine. - JDBC PreparedStatement — alimenta la mappatura delle colonne basata sui metadati con il binding sicuro dei parametri.
- JDBC Transactions —
supportsTransactions()e il supporto dei savepoint sono riportati daDatabaseMetaData.