【问题标题】:Creating a database table if it does not exist in Java production code and confirming in JUnit如果 Java 生产代码中不存在数据库表,则创建数据库表并在 JUnit 中确认
【发布时间】:2012-08-10 21:11:52
【问题描述】:

我正在用 Java 编写一个数据库程序,如果它不存在,我想创建一个表。我从How can I detect a SQL table's existence in Java? 了解到DatabaseMetaData.getTables(),我正在尝试使用它:

private boolean tableExists() throws SQLException {
    System.out.println("tableExists()");

    DatabaseMetaData dbmd = conn.getMetaData();
    ResultSet rs = dbmd.getTables(null, null, this.getTableName(), null);

    System.out.println("TABLE_NAME: " + rs.getString("TABLE_NAME"));

    return rs.getRow() == 1;
}

问题是rs.getRow() 总是返回0,即使在表创建之后也是如此。使用rs.getString("TABLE_NAME") 会引发异常,指出结果集为空。

我想到的一个可能的解决方案是执行CREATE TABLE 语句并捕获任何抛出的异常。但是,我不喜欢在程序的控制流中使用异常的想法。

FWIW,我正在使用 HSQLDB。但是,我想编写独立于 RDMS 引擎的 Java 代码。还有其他方法可以使用DatabaseMetaData.getTables() 来做我想做的事吗?还是有其他解决方案来编写我的tableExists() 方法?

添加:

使用此处给出的建议,我找到了一个似乎适用于我的生产代码的解决方案:

private void createTable() throws SQLException {
    String sqlCreate = "CREATE TABLE IF NOT EXISTS " + this.getTableName()
            + "  (brand           VARCHAR(10),"
            + "   year            INTEGER,"
            + "   number          INTEGER,"
            + "   value           INTEGER,"
            + "   card_count           INTEGER,"
            + "   player_name     VARCHAR(50),"
            + "   player_position VARCHAR(20))";

    Statement stmt = conn.createStatement();
    stmt.execute(sqlCreate);
}

现在我也在编写一个 JUnit 测试来断言该表确实已创建:

public void testConstructor() throws Exception {
    try (BaseballCardJDBCIO bcdb = new BaseballCardJDBCIO(this.url)) {
        String query = "SELECT count(*) FROM information_schema.system_tables WHERE table_name = '" + bcdb.getTableName() + "'";
        Connection conn = DriverManager.getConnection(this.url);
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(query);
        Assert.assertTrue(rs.next());
        Assert.assertEquals(1, rs.getInt(1));
        Assert.assertFalse(rs.next());
    }
}

此测试在 assertEquals() 上失败,并显示以下消息:

FAILED: expected: <1> but was: <0>

【问题讨论】:

  • 嗨,为什么不直接使用 SQL CREATE TABLE name IF NOT EXISTS (...) 呢?它更容易,如果表已经存在,则将其保留在
  • @bali182 感谢您的建议。正确的语法是CREATE TABLE IF NOT EXISTS &lt;name&gt; (&lt;columns&gt;)。现在我也在写一个 JUnit 测试。如果你有时间,一些帮助会很棒!
  • 我打赌你没有information_schema 架构的读取权限(或者它不存在)
  • @AlonsoDominguez 明天我有更多时间时会研究一下。

标签: java sql jdbc junit


【解决方案1】:

我找到的解决方案似乎有效:

private void createTable() throws SQLException {
    String sqlCreate = "CREATE TABLE IF NOT EXISTS " + this.getTableName()
            + "  (brand           VARCHAR(10),"
            + "   year            INTEGER,"
            + "   number          INTEGER,"
            + "   value           INTEGER,"
            + "   card_count           INTEGER,"
            + "   player_name     VARCHAR(50),"
            + "   player_position VARCHAR(20))";

    Statement stmt = conn.createStatement();
    stmt.execute(sqlCreate);
}

我必须将IF NOT EXISTS 放在我的 SQL 语句中的正确位置。

【讨论】:

    【解决方案2】:

    来自 Java 文档中的 ResultSet 定义:

    ResultSet 对象维护一个指向其当前行的游标 数据。最初,光标位于第一行之前。这 next 方法将光标移动到下一行,因为它返回 当 ResultSet 对象中没有更多行时为 false,它可以是 在 while 循环中用于遍历结果集。

    因此,您必须始终调用next() 方法,否则getRow() 将始终返回零,因为光标位于第一行之前。

    【讨论】:

    • 嗯...我的结果集是空的;-(
    【解决方案3】:

    内置 mysql 功能可满足您的需求:http://dev.mysql.com/doc/refman/5.0/en/create-table.html

    简而言之:只需在表创建查询的末尾附加 IF NOT EXISTS

    编辑:
    没有通用的方法来做到这一点。大多数数据库都有一个 information_scheme 表,确定信息的查询可能如下所示:

    SELECT count(*) FROM information_schema.system_tables WHERE table_schem = 'public' AND table_name = 'user';

    这适用于 sqlite、mysql、msql、mariadb 和 postgres + 可能还有很多其他的。

    【讨论】:

    • 感谢您的建议。我需要我的代码独立于任何特定的数据库引擎。
    • 噢噢噢!我不需要 RDBM 独立代码...我只需要每个数据库引擎的不同子类...grrrr
    • 事实上,这可能是最好的,因为无论如何CREATE TABLE 语法可能存在细微差别......
    • 您最初使用IF NOT EXISTS 的建议似乎有效。正确的语法是CREATE TABLE IF NOT EXISTS &lt;name&gt; (&lt;columns&gt;)
    • 嘿,我尝试使用您的 select 语句,它返回 0。我正在使用 HSQLDB。
    【解决方案4】:

    我不知道这是否一定有助于实现您的目标,但是当我使用 Python 和 MySQL 遇到这个问题时,我只是在每个“创建表”语句之前添加了一个“删除表”语句,这样就可以运行该脚本会自动删除现有表,然后重建每个表。这可能无法满足您的需求,但这是我发现成功解决类似问题的一种解决方案。

    【讨论】:

    • 这个问题的main(非常大)问题是,如果正在使用该表,则很可能其中有数据。删除表不是最好的解决方案。
    • 感谢您的建议。不幸的是,这行不通,因为最终表格中将包含我不想删除的数据。我只需要在程序第一次运行时创建表......也许某种标志会一样好......嗯......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    • 2019-03-06
    • 1970-01-01
    相关资源
    最近更新 更多