1. 概述

在这篇文章中,我们将探讨如何在JDBC环境中计算结果集(ResultSet)的行数

2. 计算 ResultSet 的行数

由于 JDBC 查询不会立即获取所有结果,因此直接计数 ResultSet 的行数并不直观。每次使用 ResultSet.next() 方法请求结果时,结果是从数据库加载的。执行 JDBC 查询时,我们无法预知会有多少结果,需要遍历所有结果,直到到达末尾才能确定可用的行数。

有两种方法可以实现这个目标:标准 ResultSet 或可滚动 ResultSet。

3. 标准 ResultSet

最直接的方法是遍历所有结果,并为每个结果递增一个计数器变量

首先,我们创建一个名为 StandardRowCounter 的类,它接受一个数据库连接作为参数:

class StandardRowCounter {
    Connection conn;

    StandardRowCounter(Connection conn) {
        this.conn = conn;
    }
}

类中包含一个方法,接受一个 SQL 查询字符串作为输入,通过遍历 ResultSet 并逐条增加计数器变量来返回行数。我们可以将这个方法命名为 getQueryRowCount

int getQueryRowCount(String query) throws SQLException {
    try (Statement statement = conn.createStatement();
        ResultSet standardRS = statement.executeQuery(query)) {
        int size = 0;
        while (standardRS.next()) {
            size++;
        }
        return size;
    }
}

请注意,我们使用了 try-with-resources 块来自动关闭 JDBC 资源。

为了测试我们的实现,我们可以利用一个内存数据库快速生成一个包含 3 条记录的表。

接下来,我们创建一个名为 RowCounterApp 的类,包含一个简单的 main 方法:

class RowCounterApp {

    public static void main(String[] args) throws SQLException {
        Connection conn = createDummyDB();

        String selectQuery = "SELECT * FROM STORAGE";

        StandardRowCounter standardCounter = new StandardRowCounter(conn);
        assert standardCounter.getQueryRowCount(selectQuery) == 3;
    }

    static Connection createDummyDB() throws SQLException {
        ...
    }

}

这段代码在任何数据库上都能工作。然而,如果数据库驱动支持,我们可以使用更高级的 API 来达到相同效果。

4. 可滚动 ResultSet

通过重载 StatementcreateStatement 方法,我们可以要求在查询执行后创建一个可滚动的 ResultSet。对于可滚动版本,我们可以使用更复杂的遍历方法,如 previous 逆序遍历。在我们的场景中,我们将使用 last 方法移动到 ResultSet 的末尾,然后使用 getRow 方法获取最后一个条目的行号。

创建一个 ScrollableRowCounter 类:

class ScrollableRowCounter {
    Connection conn;

    ScrollableRowCounter(Connection conn) {
        this.conn = conn;
    }
}

StandardRowCounter 类一样,我们仅使用数据库连接字段。

同样,我们使用 getQueryRowCount 方法:

int getQueryRowCount(String query) throws SQLException {
    try (Statement statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
        ResultSet scrollableRS = statement.executeQuery(query)) {
        scrollableRS.last();
        return scrollableRS.getRow();
    }
}

为了获取可滚动的 ResultSet,我们需要在 createStatement 方法中提供 ResultSet.TYPE_SCROLL_INSENSITIVE 常量。此外,还需要提供并发模式的值,但由于对我们的情况不相关,我们使用默认的 ResultSet.CONCUR_READ_ONLY 常量。如果 JDBC 驱动不支持这种操作模式,它会抛出异常。

让我们用 RowCountApp 测试新的实现:

ScrollableRowCounter scrollableCounter = new ScrollableRowCounter(conn);
assert scrollableCounter.getQueryRowCount(selectQuery) == 3;

5. 性能考虑

尽管上述实现简单,但它们的性能并不理想,因为必须遍历整个 ResultSet。因此,通常建议使用 COUNT 类型的查询来进行行计数操作。

例如,一个简单的示例是:

SELECT COUNT(*) FROM STORAGE

这将返回一个包含单个列的单行,其中包含 STORAGE 表中的行数。

6. 总结

在这篇文章中,我们探讨了获取 ResultSet 中行数的不同方法。

如往常一样,本文的源代码可以在 GitHub 上找到。