JDBC DatabaseMetaData.getColumns () возвращает дубликаты столбцов

StackOverflow https://stackoverflow.com/questions/1601203

  •  05-07-2019
  •  | 
  •  

Вопрос

Я занят на куске кода, чтобы получить имена столбцов таблицы из базы данных Oracle. Код, который я придумал, выглядит следующим образом:

DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection(
  "jdbc:oracle:thin:@<server>:1521:<sid>", <username>, <password>);

DatabaseMetaData meta = conn.getMetaData();
ResultSet columns = meta.getColumns(null, null, "EMPLOYEES", null);
int i = 1;
while (columns.next())
{
  System.out.printf("%d: %s (%d)\n", i++, columns.getString("COLUMN_NAME"), 
    columns.getInt("ORDINAL_POSITION"));
}

Когда я запустил этот код, к моему удивлению, было возвращено слишком много столбцов. При ближайшем рассмотрении выяснилось, что ResultSet содержит дубликат набора всех столбцов, т. Е. Каждый столбец возвращается дважды. Вот вывод, который я получил:

1: ID (1)
2: NAME (2)
3: CITY (3)
4: ID (1)
5: NAME (2)
6: CITY (3)

Когда я смотрю на таблицу с помощью Oracle SQL Developer, она показывает, что таблица имеет только три столбца (ID, NAME, CITY). Я пробовал этот код на нескольких разных таблицах в моей базе данных, и некоторые работают просто отлично, в то время как другие демонстрируют это странное поведение.

Может ли быть ошибка в драйвере JDBC Oracle? Или я здесь что-то не так делаю?

<Ч>

Обновление: благодаря Kenster Теперь у меня есть альтернативный способ получения имен столбцов , Вы можете получить их из ResultSet, например так:

DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@<server>:1521:<sid>", <username>, <password>);

Statement st = conn.createStatement();
ResultSet rset = st.executeQuery("SELECT * FROM \"EMPLOYEES\"");
ResultSetMetaData md = rset.getMetaData();
for (int i=1; i<=md.getColumnCount(); i++)
{
    System.out.println(md.getColumnLabel(i));
}

Кажется, это работает просто отлично, дубликаты не возвращаются! И для тех, кто задается вопросом: согласно этому блогу вы следует использовать getColumnLabel () вместо getColumnName ().

Это было полезно?

Решение

В oracle Connection.getMetaData() возвращает метаданные для всей базы данных , а не только схему, к которой вы подключены. Поэтому, когда вы вводите null в качестве первых двух аргументов meta.getColumns(), вы не фильтруете результаты только для своей схемы.

Вам необходимо указать имя схемы Oracle для одного из первых двух параметров <=>, возможно, второго, например

.
meta.getColumns(null, "myuser", "EMPLOYEES", null);

Это немного раздражает, но именно так люди Oracle решили реализовать свой драйвер JDBC.

Другие советы

Это не дает прямого ответа на ваш вопрос, но другой подход заключается в выполнении запроса:

select * from tablename where 1 = 0

Это вернет ResultSet, даже если он не выбирает никаких строк. Метаданные набора результатов будут соответствовать таблице, из которой вы выбрали. В зависимости от того, что вы делаете, это может быть более удобным. tablename это может быть все, что вы можете выбрать - вам не нужно корректировать регистр или беспокоиться о том, в какой схеме он находится.

В обновлении вашего вопроса я заметил, что вы пропустили одну ключевую часть ответа Кенстера. Он указал «где» предложение «где 1 = 0», которого у вас нет. Это важно, потому что если вы не включите его, то oracle попытается вернуть ВСЮ таблицу. И если вы не потянете все записи, оракул их удержит, ожидая, что вы пролистаете их. Добавление предложения where по-прежнему дает метаданные, но без каких-либо накладных расходов.

Кроме того, я лично использую 'where rownum < 1 ', поскольку oracle сразу знает, что все rownums прошли, и я не уверен, достаточно ли он умен, чтобы не пытаться проверять каждую запись на' 1 = 0 '.

В дополнение к ответу Скаффмана -

используйте следующий запрос в Oracle:

select sys_context( 'userenv', 'current_schema' ) from dual;  

чтобы получить доступ к имени вашей текущей схемы, если вы ограничены в Java.

Это поведение, предписанное API JDBC - передача нулей в качестве первого и второго параметра в getColumns означает, что ни имя каталога, ни имя схемы не используются для сужения поиска. Ссылка на документацию . Это правда, что некоторые другие драйверы JDBC по умолчанию имеют другое поведение (например, MySQL ConnectorJ по умолчанию ограничивается текущим каталогом), но это не является стандартным и задокументировано как таковое

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top