Java JDBC Метаданные
Изучите DatabaseMetaData и ResultSetMetaData в Java для получения информации о базе данных и результатах запросов во время выполнения.
Метаданные — это данные о данных: описание структуры результирующего набора и возможностей базы данных, а не сами строки. JDBC предоставляет два интерфейса метаданных, каждый из которых отвечает на разные вопросы. Они лежат в основе универсальных инструментов: средств вывода строк, браузеров схем, ORM и скриптов миграции, которые должны работать без жёстко закодированных знаний о таблицах.
В этой главе рассматриваются оба интерфейса — ResultSetMetaData (столбцы, возвращённые запросом) и DatabaseMetaData (сама база данных и драйвер) — способы чтения кодов SQL-типов, а также запускаемый пример, создающий словарь кодов типов. Предполагается, что вы уже знаете, как открыть JDBC-соединение и перебирать ResultSet.
ResultSetMetaData — описание столбцов запроса
ResultSet содержит строки, а его ResultSetMetaData — описания столбцов. Вызовите rs.getMetaData(), чтобы узнать, какие столбцы вернул запрос: сколько их, каковы их имена и SQL-типы. Именно так универсальная сетка отображает любой запрос:
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) + ")");
}Индексы столбцов начинаются с 1, а не с 0 — getColumnLabel(0) выбрасывает исключение. Наиболее полезные методы:
| Метод | Возвращает |
|---|---|
getColumnCount() | количество столбцов в результате |
getColumnLabel(i) | отображаемое имя с учётом псевдонима AS |
getColumnName(i) | исходное имя столбца без учёта псевдонимов |
getColumnType(i) | код int константы java.sql.Types (например, 4 для INTEGER) |
getColumnTypeName(i) | имя типа от поставщика (например, INT4 в PostgreSQL) |
isNullable(i) | columnNoNulls, columnNullable или columnNullableUnknown |
getPrecision(i) / getScale(i) | размер и десятичные разряды, полезно для столбцов NUMERIC |
Предпочитайте getColumnLabel вместо getColumnName при отображении заголовков: он учитывает SELECT total AS revenue, поэтому заголовок будет отображаться как revenue.
DatabaseMetaData — описание базы данных
Вызовите conn.getMetaData() для получения сведений о самой базе данных и драйвере: название и версия продукта, поддерживаемые функции, каталог таблиц, столбцов, ключей и индексов.
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"));
}Обратите внимание, что getTables, getColumns и подобные методы возвращают ResultSetы — каталог запрашивается с помощью того же API курсора, что и любые другие данные. Каждая строка getColumns имеет известные метки столбцов, которые вы читаете по имени:
// 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" : ""));
}
}Четыре аргумента getTables/getColumns — это catalog, schemaPattern, шаблон имени и (для getTables) типы таблиц. null означает «без фильтрации», а % — SQL-маска для «любого имени». Часто используемые дополнительные методы: getPrimaryKeys, getImportedKeys (внешние ключи) и getIndexInfo.
Когда использовать какой интерфейс
- Используйте
ResultSetMetaData, когда у вас есть результат запроса и нужно описать его столбцы — для просмотрщика таблиц, экспортёра CSV или маппера объектов. - Используйте
DatabaseMetaData, когда вам нужны сведения о базе данных до выполнения запроса — обнаружение функций (поддерживает ли она пакетные обновления? точки сохранения?), обнаружение схемы для инструмента миграции или ветвление SQL поgetDatabaseProductName().
Сопоставление кодов типов с именами
getColumnType(i) возвращает int — одну из констант java.sql.Types, например INTEGER (4), VARCHAR (12) или TIMESTAMP (93). Сырой int неудобно читать и использовать в switch, поэтому универсальный читатель один раз строит словарь код→имя (с помощью рефлексии над java.sql.Types) и переиспользует его для каждого результирующего набора. Этот же код подскажет, какой типизированный геттер вызывать — getInt, getString, getTimestamp — при сопоставлении столбцов с полями, как это делает маппер на основе PreparedStatement.
Практический пример: словарь кодов типов и отчёт по столбцам
Эта программа строит словарь int→имя для каждой константы java.sql.Types, а затем использует его для описания гипотетического трёхколоночного результата так, как это делал бы ResultSetMetaData — и перечисляет виды вопросов, на которые отвечает DatabaseMetaData — без живой базы данных.
Что следует вынести из запуска:
- Существует 39 стандартных кодов
java.sql.Types, и программа строит полную карту код→имя с помощью рефлексии над классомTypes. Реальный универсальный читатель строит этот словарь один раз и переиспользует его для каждого результирующего набора. ResultSetMetaData.getColumnType(i)возвращает один из этих кодовint; словарь превращает код4вINTEGER,12— вVARCHAR,93— вTIMESTAMP. Именно этот поиск позволяет инструменту отображать любой запрос без предварительного знания его столбцов.- Отчёт по каждому столбцу — имя и тип — это то, что дают
getColumnLabel/getColumnTypeдля реального запроса. Это основа просмотрщиков таблиц, экспортёров CSV и ORM, которые сопоставляют столбцы с полями. DatabaseMetaDataотвечает на другой класс вопросов: не «что вернул этот запрос», а «что умеет эта база данных» — её название, версию драйвера, поддержку функций и каталог таблиц.- Что важно, методы каталога (
getTables,getColumns) возвращаютResultSetы, поэтому структуру базы данных вы читаете с помощью того же цикла с курсором, что и данные. Метаданные — это не специальный API, это данные о данных, предоставляемые тем же способом.
java.sql.Types (здесь 39) может немного варьироваться в зависимости от версии JDK по мере добавления новых констант. Строите словарь с помощью рефлексии, как показано выше, а не жёстко задавайте количество.Что изучить далее
- JDBC ResultSet — API курсора, который возвращают как
getMetaData(), так и методы каталога. - JDBC Connection — откуда берётся
getMetaData()для соединения. - JDBC PreparedStatement — обеспечивает сопоставление столбцов на основе метаданных с безопасной привязкой параметров.
- JDBC Transactions —
supportsTransactions()и поддержка точек сохранения сообщаются черезDatabaseMetaData.