W3docs

Java JDBC Metadata

Inspecione bancos de dados e conjuntos de resultados em tempo de execução no Java com DatabaseMetaData e ResultSetMetaData.

Metadata são dados sobre os dados — a estrutura de um conjunto de resultados e as capacidades do banco de dados, e não as próprias linhas. O JDBC expõe duas interfaces de metadata, e elas respondem a perguntas diferentes. Elas alimentam ferramentas genéricas: impressoras de linhas, navegadores de esquemas, ORMs e scripts de migração que precisam funcionar sem conhecimento fixo das tabelas.

Este capítulo aborda as duas interfaces — ResultSetMetaData (as colunas que uma consulta retornou) e DatabaseMetaData (o banco de dados e o driver em si) — como ler códigos de tipo SQL e um exemplo executável que constrói um dicionário de códigos de tipo. Pressupõe que você já sabe como abrir uma conexão JDBC e iterar um ResultSet.

ResultSetMetaData — descrevendo as colunas de uma consulta

Um ResultSet carrega as linhas; seu ResultSetMetaData carrega as descrições das colunas. Chame rs.getMetaData() para descobrir quais colunas uma consulta retornou: quantas, seus nomes e seus tipos SQL. É assim que uma grade genérica renderiza qualquer 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) + ")");
}

Os índices de coluna são baseados em 1, não em 0 — getColumnLabel(0) lança uma exceção. Os métodos mais úteis:

MétodoRetorna
getColumnCount()número de colunas no resultado
getColumnLabel(i)nome de exibição, respeitando qualquer alias AS
getColumnName(i)nome da coluna subjacente, ignorando aliases
getColumnType(i)o código int de java.sql.Types (ex.: 4 para INTEGER)
getColumnTypeName(i)o nome do tipo do fornecedor (ex.: INT4 no PostgreSQL)
isNullable(i)columnNoNulls, columnNullable ou columnNullableUnknown
getPrecision(i) / getScale(i)tamanho e dígitos decimais, útil para colunas NUMERIC

Prefira getColumnLabel a getColumnName ao exibir cabeçalhos: ele respeita SELECT total AS revenue, portanto o cabeçalho exibirá revenue.

DatabaseMetaData — descrevendo o banco de dados

Chame conn.getMetaData() para obter informações sobre o banco de dados e o driver em si: nome e versão do produto, funcionalidades suportadas e o catálogo de tabelas, colunas, chaves 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"));
}

Observe que getTables, getColumns e métodos semelhantes retornam ResultSets — o catálogo é consultado com a mesma API de cursor que qualquer outro dado. Cada linha de getColumns possui rótulos de coluna bem conhecidos que você lê pelo 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" : ""));
  }
}

Os quatro argumentos para getTables/getColumns são catalog, schemaPattern, um padrão de nome e (para getTables) os tipos de tabela. null significa "não filtrar", e % é o curinga SQL para "qualquer nome". Companheiros comuns são getPrimaryKeys, getImportedKeys (chaves estrangeiras) e getIndexInfo.

Quando usar cada interface

  • Use ResultSetMetaData quando você tiver um resultado de consulta e precisar descrever suas colunas — um visualizador de grade, um exportador de CSV, um mapeador de objetos.
  • Use DatabaseMetaData quando precisar de informações sobre o banco de dados antes de consultá-lo — detecção de recursos (ele suporta atualizações em lote? savepoints?), descoberta de esquema para uma ferramenta de migração, ou ramificação de SQL com getDatabaseProductName().

Mapeando códigos de tipo para nomes

getColumnType(i) retorna um int — uma das constantes de java.sql.Types como INTEGER (4), VARCHAR (12) ou TIMESTAMP (93). O int bruto é difícil de ler e de usar em um switch, portanto um leitor genérico constrói um dicionário de código para nome uma vez (refletindo sobre java.sql.Types) e o reutiliza para cada conjunto de resultados. O mesmo código também indica qual getter tipado chamar — getInt, getString, getTimestamp — ao mapear colunas para campos, da forma como um mapeador orientado por PreparedStatement faz.

Um exemplo prático: um dicionário de códigos de tipo e um relatório de colunas

Este programa constrói o dicionário int→nome para cada constante de java.sql.Types, depois o usa para descrever um hipotético resultado de três colunas da maneira que ResultSetMetaData faria — e lista os tipos de perguntas que DatabaseMetaData responde — sem um banco de dados ativo.

java— editable, runs on the server

O que aprender com a execução:

  • Há 39 códigos java.sql.Types padrão, e o programa constrói o mapa completo de código→nome refletindo sobre a classe Types. Um leitor genérico real constrói este dicionário uma vez e o reutiliza para cada conjunto de resultados.
  • ResultSetMetaData.getColumnType(i) retorna um desses códigos int; o dicionário converte o código 4 em INTEGER, 12 em VARCHAR, 93 em TIMESTAMP. Essa consulta é exatamente o que permite que uma ferramenta renderize qualquer consulta sem conhecer suas colunas antecipadamente.
  • O relatório por coluna — nome mais tipo — é o que getColumnLabel/getColumnType fornecem para uma consulta real. É a base de visualizadores de grade, exportadores de CSV e ORMs que mapeiam colunas para campos.
  • DatabaseMetaData responde a uma classe diferente de perguntas: não "o que esta consulta retornou", mas "o que este banco de dados pode fazer" — seu nome de produto, versão do driver, suporte a recursos e catálogo de tabelas.
  • Crucialmente, os métodos de catálogo (getTables, getColumns) retornam ResultSets, portanto você lê a estrutura do banco de dados com o mesmo loop de cursor que usa para dados. Metadata não é uma API especial — são dados sobre dados, entregues da mesma forma.
Nota
O número de códigos java.sql.Types (39 aqui) pode variar ligeiramente entre versões do JDK à medida que novas constantes são adicionadas. Construa o dicionário por reflexão, como acima, em vez de fixar a contagem no código.

Próximos passos

  • JDBC ResultSet — a API de cursor que tanto getMetaData() quanto os métodos de catálogo retornam.
  • JDBC Connection — onde getMetaData() na conexão se origina.
  • JDBC PreparedStatement — alimente o mapeamento de colunas orientado por metadata com vinculação segura de parâmetros.
  • JDBC TransactionssupportsTransactions() e suporte a savepoints são reportados por DatabaseMetaData.

Prática

Prática
Você está escrevendo uma ferramenta genérica que deve imprimir os nomes e tipos das colunas de qualquer consulta SQL recebida, sem conhecimento prévio do esquema. Qual interface fornece essas informações?
Você está escrevendo uma ferramenta genérica que deve imprimir os nomes e tipos das colunas de qualquer consulta SQL recebida, sem conhecimento prévio do esquema. Qual interface fornece essas informações?
Was this page helpful?