JDBC DatabaseMetaData.getColumns () retorna colunas duplicadas
-
05-07-2019 - |
Pergunta
Estou ocupado em um pedaço de código para obter alle os nomes de coluna de uma tabela de um banco de dados Oracle. O código que eu vim com esta aparência:
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"));
}
Quando eu corri este código para minha surpresa muitas colunas foram devolvidos. Um olhar mais atento revela que o ResultSet continha um conjunto duplicado de todas as colunas, ou seja, cada coluna foi devolvido duas vezes. Aqui está a saída que eu tenho:
1: ID (1)
2: NAME (2)
3: CITY (3)
4: ID (1)
5: NAME (2)
6: CITY (3)
Quando eu olhar para a tabela usando o Oracle SQL Developer isso mostra que a tabela só tem três colunas (ID, nome, cidade). Eu tentei este código contra várias tabelas diferentes no meu banco de dados e alguns trabalhos muito bem, enquanto outros apresentam esse comportamento estranho.
Pode haver um bug no driver JDBC Oracle? Ou estou fazendo algo errado aqui?
Update: Graças ao Kenster agora tenho uma maneira alternativa para recuperar os nomes das colunas . Você pode obtê-los a partir de um ResultSet, como este:
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));
}
Isso parece funcionar muito bem e há duplicatas são devolvidos! E para aqueles que se perguntam: de acordo com neste blog você deve usar getColumnLabel () em vez de getColumnName ().
Solução
Na Oracle, Connection.getMetaData()
retorna meta-dados para o inteira banco de dados, não apenas o esquema tiver que ser conectado. Então, quando você fornecer null
como os dois primeiros argumentos para meta.getColumns()
, você não está filtrando os resultados para apenas o seu esquema.
Você precisa fornecer o nome do esquema do Oracle a um dos dois primeiros parâmetros de meta.getColumns()
, provavelmente, o segundo, por exemplo.
meta.getColumns(null, "myuser", "EMPLOYEES", null);
É um pouco irritante ter que fazer isso, mas essa é a maneira como as pessoas da Oracle optou por implementar o seu driver JDBC.
Outras dicas
Esta não responder diretamente sua pergunta, mas uma outra abordagem é para executar a consulta:
select * from tablename where 1 = 0
Isso irá retornar um ResultSet, embora não selecionar nenhuma linha. Os metadados conjunto de resultados irá coincidir com a tabela que você selecionou. Dependendo do que você está fazendo, isso pode ser mais conveniente. tablename
pode ser qualquer coisa que você pode selecionar em -. você não tem que ter o caso correto ou se preocupar com o esquema que está em
Na actualização à sua pergunta eu notei que você perdeu uma parte chave da resposta de Kenster. Ele especificou uma cláusula 'onde' de 'onde 1 = 0', que você não tem. Isto é importante porque se você deixá-lo fora, em seguida, a Oracle vai tentar voltar a tabela inteira. E se você não puxar todos os registros sobre, oracle vai realizar-lhes, esperando por você para página através deles. Acrescentando que onde cláusula ainda lhe dá os metadados, mas sem qualquer um dos acima.
Além disso, eu pessoalmente uso 'onde rownum <1', desde que a Oracle sabe imediatamente que todos os rownums são passado que, e eu não tenho certeza se ele é inteligente o suficiente para não experimentar e testar cada registro para '1 = 0'.
Além de resposta de skaffman -
usar a seguinte consulta no Oracle:
select sys_context( 'userenv', 'current_schema' ) from dual;
para acessar o nome do esquema atual, se você está restrito a fazê-lo em Java.
Este é o comportamento estipulado pela API JDBC - passando nulos como primeiro e segundo parâmetro para meios GetColumns que nem nome de catálogo nem nome de esquema são usados ??para restringir a pesquisa. link para a documentação . É verdade que alguns outros drivers JDBC tem um comportamento diferente por padrão (ConnectorJ de exemplo MySQL por restringe padrão para o catálogo atual), mas isso não é padrão, e documentado como tal