W3docs

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étodoDevuelve
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 ResultSetMetaData cuando tengas un resultado de consulta y necesites describir sus columnas — un visor de cuadrícula, un exportador CSV, un mapeador de objetos.
  • Usa DatabaseMetaData cuando 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ún getDatabaseProductName().

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.

java— editable, runs on the server

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 clase Types. 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ódigos int; el diccionario convierte el código 4 en INTEGER, 12 en VARCHAR, 93 en TIMESTAMP. 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/getColumnType proporcionan para una consulta real. Es la base de los visores de cuadrícula, exportadores CSV y ORMs que mapean columnas a campos.
  • DatabaseMetaData responde 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) devuelven ResultSets, 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.
Nota
El número de códigos de 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 TransactionssupportsTransactions() y el soporte de puntos de guardado son reportados por DatabaseMetaData.

Práctica

Práctica
Estás escribiendo una herramienta genérica que debe imprimir los nombres y tipos de columnas de cualquier consulta SQL que reciba, sin conocimiento previo del esquema. ¿Qué interfaz te proporciona esa información?
Estás escribiendo una herramienta genérica que debe imprimir los nombres y tipos de columnas de cualquier consulta SQL que reciba, sin conocimiento previo del esquema. ¿Qué interfaz te proporciona esa información?
Was this page helpful?