Metadatos JDBC en Java
Inspecciona bases de datos y conjuntos de resultados en tiempo de ejecución con DatabaseMetaData y ResultSetMetaData en Java.
Los metadatos son datos sobre los datos — la estructura de un conjunto de resultados y las capacidades de la base de datos, no las filas en sí. JDBC expone dos interfaces de metadatos, y cada una responde preguntas diferentes. Son la base de herramientas genéricas: impresoras de filas, navegadores de esquemas, ORMs y scripts de migración que deben funcionar sin conocimiento codificado de las tablas.
Este capítulo cubre ambas interfaces — ResultSetMetaData (las columnas que devolvió una consulta) y DatabaseMetaData (la base de datos y el controlador en sí) —, cómo leer los códigos de tipo SQL y un ejemplo ejecutable que construye un diccionario de códigos de tipo. Se asume que ya sabes cómo abrir una conexión JDBC e iterar un ResultSet.
ResultSetMetaData — descripción de las columnas de una consulta
Un ResultSet contiene las filas; su ResultSetMetaData contiene las descripciones de las columnas. Llama a rs.getMetaData() para conocer qué columnas devolvió una consulta: cuántas hay, sus nombres y sus tipos SQL. Así es como una cuadrícula genérica renderiza cualquier consulta:
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) + ")");
}Los índices de columna son de base 1, no de base 0 — getColumnLabel(0) lanza una excepción. Los métodos más útiles:
| Método | Devuelve |
|---|---|
getColumnCount() | número de columnas en el resultado |
getColumnLabel(i) | nombre de visualización, respetando cualquier alias AS |
getColumnName(i) | nombre de columna subyacente, ignorando alias |
getColumnType(i) | el código int de java.sql.Types (p. ej., 4 para INTEGER) |
getColumnTypeName(i) | el nombre de tipo del proveedor (p. ej., INT4 en PostgreSQL) |
isNullable(i) | columnNoNulls, columnNullable o columnNullableUnknown |
getPrecision(i) / getScale(i) | tamaño y dígitos decimales, útil para columnas NUMERIC |
Prefiere getColumnLabel sobre getColumnName al mostrar encabezados: respeta SELECT total AS revenue, por lo que el encabezado muestra revenue.
DatabaseMetaData — descripción de la base de datos
Llama a conn.getMetaData() para obtener información sobre la base de datos y el controlador: nombre y versión del producto, características compatibles y el catálogo de tablas, columnas, claves e índices.
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"));
}Observa que getTables, getColumns y métodos similares devuelven ResultSets — el catálogo se consulta con la misma API de cursor que cualquier otro dato. Cada fila de getColumns tiene etiquetas de columna bien conocidas que se leen por nombre:
// 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" : ""));
}
}Los cuatro argumentos de getTables/getColumns son catalog, schemaPattern, un patrón de nombre y (para getTables) los tipos de tabla. null significa "no filtrar" y % es el comodín SQL para "cualquier nombre". Los compañeros habituales son getPrimaryKeys, getImportedKeys (claves foráneas) y getIndexInfo.
Cuándo usar cada interfaz
- Usa
ResultSetMetaDatacuando tengas un resultado de consulta y necesites describir sus columnas — un visor de cuadrícula, un exportador CSV, un mapeador de objetos. - Usa
DatabaseMetaDatacuando necesites información sobre la base de datos antes de consultarla — detección de características (¿soporta actualizaciones por lote? ¿puntos de guardado?), descubrimiento de esquema para una herramienta de migración, o ramificación SQL segúngetDatabaseProductName().
Mapeo de códigos de tipo a nombres
getColumnType(i) devuelve un int — una de las constantes de java.sql.Types como INTEGER (4), VARCHAR (12) o TIMESTAMP (93). El int sin procesar es incómodo de leer y de usar en switch, así que un lector genérico construye un diccionario código-a-nombre una sola vez (reflexionando sobre java.sql.Types) y lo reutiliza para cada conjunto de resultados. El mismo código también indica qué getter tipado llamar — getInt, getString, getTimestamp — al mapear columnas a campos, igual que hace un mapeador impulsado por PreparedStatement.
Un ejemplo práctico: un diccionario de códigos de tipo y un informe de columnas
Este programa construye el diccionario int→nombre para cada constante de java.sql.Types, luego lo usa para describir un hipotético resultado de tres columnas tal como lo haría ResultSetMetaData — y enumera los tipos de preguntas que responde DatabaseMetaData — sin necesidad de una base de datos activa.
Lo que extraer de la ejecución:
- Existen 39 códigos estándar en
java.sql.Types, y el programa construye todo el mapa código→nombre reflexionando sobre la claseTypes. Un lector genérico real construye este diccionario una vez y lo reutiliza para cada conjunto de resultados. ResultSetMetaData.getColumnType(i)devuelve uno de estos códigosint; el diccionario convierte el código4enINTEGER,12enVARCHAR,93enTIMESTAMP. Esa búsqueda es exactamente lo que permite a una herramienta renderizar cualquier consulta sin conocer sus columnas de antemano.- El informe por columna — nombre más tipo — es lo que
getColumnLabel/getColumnTypeproporcionan para una consulta real. Es la base de los visores de cuadrícula, exportadores CSV y ORMs que mapean columnas a campos. DatabaseMetaDataresponde una clase diferente de preguntas: no "qué devolvió esta consulta" sino "qué puede hacer esta base de datos" — su nombre de producto, versión del controlador, soporte de características y catálogo de tablas.- De manera crucial, los métodos de catálogo (
getTables,getColumns) devuelvenResultSets, así que lees la estructura de la base de datos con el mismo bucle de cursor que usas para los datos. Los metadatos no son una API especial — son datos sobre datos, entregados de la misma manera.
java.sql.Types (39 aquí) puede variar ligeramente entre versiones de JDK a medida que se añaden nuevas constantes. Construye el diccionario mediante reflexión, como se muestra arriba, en lugar de codificar el conteo de manera fija.Qué ver a continuación
- JDBC ResultSet — la API de cursor que devuelven tanto
getMetaData()como los métodos de catálogo. - JDBC Connection — de dónde proviene
getMetaData()en la conexión. - JDBC PreparedStatement — alimenta el mapeo de columnas basado en metadatos con enlace de parámetros seguro.
- JDBC Transactions —
supportsTransactions()y el soporte de puntos de guardado son reportados porDatabaseMetaData.