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