JDBC DatabaseMetaData.getColumns () возвращает дубликаты столбцов - PullRequest
17 голосов
/ 21 октября 2009

Я занят на куске кода, чтобы получить все имена столбцов таблицы из базы данных 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). Я пробовал этот код на нескольких разных таблицах в моей базе данных, и некоторые работают просто отлично, в то время как другие демонстрируют это странное поведение.

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


Обновление: Благодаря 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 ().

Ответы [ 5 ]

25 голосов
/ 21 октября 2009

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

Вам необходимо указать имя схемы Oracle для одного из первых двух параметров meta.getColumns(), возможно, второго, например,

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

Немного раздражает необходимость делать это, но именно так Oracle решили использовать свой драйвер JDBC.

15 голосов
/ 22 октября 2009

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

select * from tablename where 1 = 0

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

3 голосов
/ 31 августа 2011

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

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

1 голос
/ 23 августа 2012

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

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

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

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

0 голосов
/ 21 января 2013

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

...