JDBC DatabaseMetaData.getColumns() возвращает повторяющиеся столбцы

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

Ответы

Ответ 1

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

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

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

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

Ответ 2

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

select * from tablename where 1 = 0

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

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

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