Métadonnées JDBC en Java
Inspectez les bases de données et les ensembles de résultats à l'exécution en Java avec DatabaseMetaData et ResultSetMetaData.
Les métadonnées sont des données sur les données — la structure d'un ensemble de résultats et les capacités de la base de données, plutôt que les lignes elles-mêmes. JDBC expose deux interfaces de métadonnées, et elles répondent à des questions différentes. Elles alimentent les outils génériques : afficheurs de lignes, navigateurs de schémas, ORM et scripts de migration qui doivent fonctionner sans connaissance codée en dur des tables.
Ce chapitre couvre les deux interfaces — ResultSetMetaData (les colonnes renvoyées par une requête) et DatabaseMetaData (la base de données et le pilote eux-mêmes) — comment lire les codes de types SQL, et un exemple exécutable qui construit un dictionnaire de codes de types. Il suppose que vous savez déjà comment ouvrir une connexion JDBC et itérer un ResultSet.
ResultSetMetaData — décrire les colonnes d'une requête
Un ResultSet contient les lignes ; son ResultSetMetaData contient les descriptions des colonnes. Appelez rs.getMetaData() pour connaître les colonnes renvoyées par une requête : combien, leurs noms et leurs types SQL. C'est ainsi qu'une grille générique affiche n'importe quelle requête :
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) + ")");
}Les index de colonnes sont basés sur 1, pas sur 0 — getColumnLabel(0) lève une exception. Les méthodes les plus utiles :
| Méthode | Retourne |
|---|---|
getColumnCount() | le nombre de colonnes dans le résultat |
getColumnLabel(i) | le nom d'affichage, tenant compte de tout alias AS |
getColumnName(i) | le nom de colonne sous-jacent, ignorant les alias |
getColumnType(i) | le code int de java.sql.Types (ex. 4 pour INTEGER) |
getColumnTypeName(i) | le nom de type du fournisseur (ex. INT4 sur PostgreSQL) |
isNullable(i) | columnNoNulls, columnNullable, ou columnNullableUnknown |
getPrecision(i) / getScale(i) | taille et chiffres décimaux, utile pour les colonnes NUMERIC |
Préférez getColumnLabel à getColumnName lorsque vous affichez des en-têtes : il respecte SELECT total AS revenue, donc l'en-tête affiche revenue.
DatabaseMetaData — décrire la base de données
Appelez conn.getMetaData() pour obtenir des informations sur la base de données et le pilote eux-mêmes : le nom et la version du produit, les fonctionnalités prises en charge, et le catalogue des tables, colonnes, clés et index.
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"));
}Notez que getTables, getColumns et leurs homologues retournent des ResultSets — le catalogue est interrogé avec la même API de curseur que toute autre donnée. Chaque ligne de getColumns a des libellés de colonnes bien définis que vous lisez par nom :
// 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" : ""));
}
}Les quatre arguments de getTables/getColumns sont catalog, schemaPattern, un motif de nom et (pour getTables) les types de table. null signifie « ne pas filtrer », et % est le caractère générique SQL pour « n'importe quel nom ». Les méthodes complémentaires courantes sont getPrimaryKeys, getImportedKeys (clés étrangères) et getIndexInfo.
Quelle interface utiliser
- Utilisez
ResultSetMetaDatalorsque vous avez un résultat de requête et devez décrire ses colonnes — un afficheur de grille, un exporteur CSV, un mappeur d'objets. - Utilisez
DatabaseMetaDatalorsque vous avez besoin d'informations sur la base de données avant de l'interroger — détection de fonctionnalités (prend-elle en charge les mises à jour par lot ? les points de sauvegarde ?), découverte de schéma pour un outil de migration, ou branchement SQL pargetDatabaseProductName().
Correspondance entre codes de types et noms
getColumnType(i) renvoie un int — l'une des constantes de java.sql.Types comme INTEGER (4), VARCHAR (12) ou TIMESTAMP (93). Le int brut est difficile à lire et à utiliser dans un switch, donc un lecteur générique construit une fois un dictionnaire code-vers-nom (en réfléchissant sur java.sql.Types) et le réutilise pour chaque ensemble de résultats. Le même code vous indique également quel getter typé appeler — getInt, getString, getTimestamp — lorsque vous mappez des colonnes vers des champs, de la même façon qu'un mappeur piloté par PreparedStatement le fait.
Exemple concret : un dictionnaire de codes de types et un rapport de colonnes
Ce programme construit le dictionnaire int→nom pour chaque constante de java.sql.Types, puis l'utilise pour décrire un résultat hypothétique à trois colonnes comme le ferait ResultSetMetaData — et liste les types de questions auxquelles DatabaseMetaData répond — sans base de données active.
Ce que l'on retient de l'exécution :
- Il existe 39 codes standard
java.sql.Types, et le programme construit l'intégralité de la correspondance code→nom en réfléchissant sur la classeTypes. Un vrai lecteur générique construit ce dictionnaire une fois et le réutilise pour chaque ensemble de résultats. ResultSetMetaData.getColumnType(i)renvoie l'un de ces codesint; le dictionnaire transforme le code4enINTEGER,12enVARCHAR,93enTIMESTAMP. Cette recherche est exactement ce qui permet à un outil d'afficher n'importe quelle requête sans connaître ses colonnes à l'avance.- Le rapport par colonne — nom et type — est ce que
getColumnLabel/getColumnTypevous fournissent pour une vraie requête. C'est le fondement des afficheurs de grilles, des exporteurs CSV et des ORM qui mappent les colonnes vers les champs. DatabaseMetaDatarépond à une autre catégorie de questions : non pas « qu'a renvoyé cette requête » mais « qu'est-ce que cette base de données peut faire » — son nom de produit, la version du pilote, la prise en charge des fonctionnalités et le catalogue de tables.- De façon notable, les méthodes de catalogue (
getTables,getColumns) retournent desResultSets, donc vous lisez la structure de la base de données avec exactement la même boucle de curseur que vous utilisez pour les données. Les métadonnées ne sont pas une API spéciale — ce sont des données sur des données, livrées de la même façon.
java.sql.Types (39 ici) peut varier légèrement entre les versions du JDK à mesure que de nouvelles constantes sont ajoutées. Construisez le dictionnaire par réflexion, comme ci-dessus, plutôt que de coder en dur le nombre.Prochaines étapes
- JDBC ResultSet — l'API de curseur que
getMetaData()et les méthodes de catalogue retournent. - JDBC Connection — d'où provient
getMetaData()sur la connexion. - JDBC PreparedStatement — alimentez le mappage de colonnes piloté par les métadonnées avec une liaison sûre des paramètres.
- JDBC Transactions —
supportsTransactions()et la prise en charge des points de sauvegarde sont signalés parDatabaseMetaData.