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)。我已经针对数据库中的几个不同的表尝试了这段代码,有些工作得很好,而另一些则表现出这种奇怪的行为。
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()。
I'm busy on a piece of code to get alle the column names of a table from an Oracle database. The code I came up with looks like this:
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"));
}
When I ran this code to my surprise too many columns were returned. A closer look revealed that the ResultSet contained a duplicate set of all the columns, i.e. every column was returned twice. Here's the output I got:
1: ID (1)
2: NAME (2)
3: CITY (3)
4: ID (1)
5: NAME (2)
6: CITY (3)
When I look at the table using Oracle SQL Developer it shows that the table only has three columns (ID, NAME, CITY). I've tried this code against several different tables in my database and some work just fine, while others exhibit this weird behaviour.
Could there be a bug in the Oracle JDBC driver? Or am I doing something wrong here?
Update: Thanks to Kenster I now have an alternative way to retrieve the column names. You can get them from a ResultSet, like this:
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));
}
This seems to work just fine and no duplicates are returned! And for those who wonder: according to this blog you should use getColumnLabel() instead of getColumnName().
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在 Oracle 中,
Connection.getMetaData()
返回整个数据库的元数据,而不仅仅是您碰巧连接到的模式。因此,当您提供null
作为meta.getColumns()
的前两个参数时,您不会仅针对您的架构过滤结果。您需要将 Oracle 模式的名称提供给
meta.getColumns()
的前两个参数之一,可能是第二个,例如,必须这样做有点烦人,但就是这样Oracle 人员选择实现他们的 JDBC 驱动程序。
In oracle,
Connection.getMetaData()
returns meta-data for the entire database, not just the schema you happen to be connected to. So when you supplynull
as the first two arguments tometa.getColumns()
, you're not filtering the results for just your schema.You need to supply the name of the Oracle schema to one of the first two parameters of
meta.getColumns()
, probably the second one, e.g.It's a bit irritating having to do this, but that's the way the Oracle folks chose to implement their JDBC driver.
这并不能直接回答您的问题,但另一种方法是执行查询:
这将返回一个 ResultSet,即使它不选择任何行。结果集元数据将与您从中选择的表相匹配。根据您正在做什么,这可能会更方便。
tablename
可以是您可以选择的任何内容——您不必确保大小写正确或担心它所在的模式。This doesn't directly answer your question, but another approach is to execute the query:
This will return a ResultSet, even though it doesn't select any rows. The result set metadata will match the table that you selected from. Depending on what you're doing, this can be more convenient.
tablename
can be anything that you can select on--you don't have to get the case correct or worry about what schema it's in.在更新你的问题时,我注意到你错过了肯斯特答案的一个关键部分。他指定了“where 1 = 0”的“where”子句,而您没有。这很重要,因为如果您忽略它,那么 Oracle 将尝试返回整个表。如果您不将所有记录拉过来,Oracle 将保留它们,等待您翻阅它们。添加该 where 子句仍然可以为您提供元数据,但没有任何开销。
另外,我个人使用 'where rownum <; 1',因为 Oracle 立即知道所有 rownums 都超过了这个值,并且我不确定它是否足够聪明,不尝试测试每个记录是否为“1 = 0”。
In the update to your question I noticed that you missed one key part of Kenster's answer. He specified a 'where' clause of 'where 1 = 0', which you don't have. This is important because if you leave it off, then oracle will try and return the ENTIRE table. And if you don't pull all of the records over, oracle will hold unto them, waiting for you to page through them. Adding that where clause still gives you the metadata, but without any of the overhead.
Also, I personally use 'where rownum < 1', since oracle knows immediately that all rownums are past that, and I'm not sure if it's smart enough to not try and test each record for '1 = 0'.
除了 skaffman 的答案之外 -
在 Oracle 中使用以下查询:
如果您在 Java 中被限制这样做,则可以访问您当前的模式名称。
In addition to skaffman's answer -
use the following query in Oracle:
to access your current schema name if you are restricted to do so in Java.
这是 JDBC API 强制执行的行为 - 将 null 作为第一个和第二个参数传递给 getColumns 意味着既不使用目录名称也不使用架构名称来缩小搜索范围。
链接到文档 。确实,其他一些 JDBC 驱动程序默认情况下有不同的行为(例如 MySQL 的 ConnectorJ 默认限制为当前目录),但这不是标准的,并且是这样记录的
This is the behavior mandated by the JDBC API - passing nulls as first and second parameter to getColumns means that neither catalog name nor schema name are used to narrow the search.
Link to the documentation . It is true that some other JDBC drivers have different behavior by default (e.g MySQL's ConnectorJ by default restricts to the current catalog), but this is not standard, and documented as such