代码之家  ›  专栏  ›  技术社区  ›  LeonZandman

jdbc databasemetadata.getcolumns()返回重复的列

  •  17
  • LeonZandman  · 技术社区  · 16 年前

    我正忙于编写一段代码,以便从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"));
    }
    

    当我运行此代码时,意外地返回了太多的列。仔细观察发现,结果集包含所有列的重复集,即每列返回两次。这是我得到的输出:

    1: ID (1)
    2: NAME (2)
    3: CITY (3)
    4: ID (1)
    5: NAME (2)
    6: CITY (3)
    

    当我使用OracleSQLDeveloper查看该表时,它显示该表只有三列(id、name、city)。我在我的数据库中的几个不同的表上尝试了这段代码,其中一些工作正常,而另一些则表现出这种奇怪的行为。

    Oracle JDBC驱动程序中是否存在错误?还是我在这里做错了什么?


    更新: 多亏了 Kenster 现在我有了一种检索列名的替代方法。您可以从结果集中获取它们,如下所示:

    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 blog 您应该使用getColumnLabel()而不是getColumnName()。

    5 回复  |  直到 13 年前
        1
  •  24
  •   skaffman    16 年前

    在Oracle中, Connection.getMetaData() 返回的元数据 整个的 数据库,而不仅仅是您碰巧连接到的模式。所以当你供应 null 作为前两个论点 meta.getColumns() ,您不只是为您的模式过滤结果。

    您需要将Oracle模式的名称提供给 meta.getcolumns()。 可能是第二个,例如

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

    这样做有点让人恼火,但这正是甲骨文人员选择实现JDBC驱动程序的方式。

        2
  •  15
  •   Kenster marc_s    16 年前

    这不会直接回答您的问题,但另一种方法是执行查询:

    select * from tablename where 1 = 0
    

    这将返回一个结果集,即使它没有选择任何行。结果集元数据将与从中选择的表匹配。根据你在做什么,这会更方便。 tablename 可以是您可以选择的任何内容——您不必纠正大小写,也不必担心它在哪个模式中。

        3
  •  3
  •   Jeffrey Harmon    14 年前

    在更新你的问题时,我注意到你错过了肯斯特答案的一个关键部分。他指定了“Where 1=0”的“Where”子句,但您没有。这一点很重要,因为如果您不使用它,那么Oracle将尝试返回整个表。如果你不把所有的记录都翻过来,神谕会把它们保存起来,等着你翻阅。添加WHERE子句仍然可以提供元数据,但不会产生任何开销。

    此外,我个人使用“Where RowNum<1”,因为Oracle立即知道所有RowNum都已过了这一步,我不确定它是否足够聪明,不尝试测试“1=0”的每个记录。

        4
  •  1
  •   ujace    13 年前

    除了斯卡夫曼的回答-

    在Oracle中使用以下查询:

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

    如果在Java中受限于此,则访问当前的架构名称。

        5
  •  0
  •   Vladislav Vaintroub    13 年前

    这是JDBCAPI授权的行为——将nulls作为第一个和第二个参数传递给getColumns意味着既不使用目录名也不使用模式名来缩小搜索范围。 Link to the documentation .确实,其他一些JDBC驱动程序在默认情况下具有不同的行为(例如,mysql的connectorj在默认情况下仅限于当前目录),但这不是标准的,并且记录在案