Java JDBC中的分页

时间:2016-10-11 15:16:47

标签: java oracle jdbc pagination

我有一张包含数百万条记录的表格。为了使系统更快,我需要在我的Java代码中实现分页概念。我需要一次只获取1000条记录并处理它们,然后选择另外1000条记录并进行处理等等。我已经尝试了一些东西,但没有一个在起作用。我试过的一些事情列在下面 -

1) String query = "select * from TABLENAME" +  " WHERE ROWNUM BETWEEN %d AND       %d";
sql = String.format(query, firstrow, firstrow + rowcount);

在上面的示例中,当查询为SELECT * from TABLENAME Where ROWNUM BETWEEN 0 and 10时,它会给我一个结果,但当查询为SELECT * from TABLENAME Where ROWNUM BETWEEN 10 and 20时,它会返回一个空结果集。我甚至试图在DB中运行它,它返回Empty结果集(不知道为什么!!)

2)preparedStatement.setFetchSize(100);我在我的Java代码中有这个,但它仍然从表中获取所有记录。无论如何,添加此语句并不会影响我的代码。

请帮忙!

2 个答案:

答案 0 :(得分:6)

听起来您实际上并不需要对结果进行分页,而只是批量处理结果。如果是这种情况,那么您需要做的就是使用setFetchSize将获取大小设置为1000并像往常一样迭代结果集(使用resultset.next())并在迭代时处理结果。有许多资源描述了setFetchSize及其功能。做一些研究:

对于oracle分页,有很多资源描述了如何执行此操作。只需进行网络搜索。以下是一些描述如何操作的资源:

如果您没有定义一致的排序(ORDER BY子句),则分页不是很有用,因为您不能依赖它们返回的顺序。
这个答案解释了为什么BETWEEN声明不起作用:https://stackoverflow.com/a/10318244/908961

如果使用超过12c的oracle,您需要进行子选择以获得结果。类似的东西:

SELECT c.*
FROM (SELECT c.*, ROWNUM as rnum
      FROM (SELECT * FROM TABLENAME ORDER BY id) c) c
WHERE c.rnum BETWEEN %d AND %d

如果您使用的是Oracle 12c或更高版本,我建议您使用较新的OFFSET FETCH语法,而不是摆弄rownum。请参阅上面的第一个链接或 http://www.toadworld.com/platforms/oracle/b/weblog/archive/2016/01/23/oracle-12c-enhanced-syntax-for-row-limiting-a-k-a-top-n-queries

所以你的查询就像

String query = "select * from TABLENAME OFFSET %d ROWS FETCH NEXT 1000 ONLY";
String.format(query, firstrow);

或使用预备陈述

PreparedStatement statement = con.prepareStatement("select * from TABLENAME OFFSET ? ROWS FETCH NEXT 1000 ONLY");
statement.setInt(1, firstrow);
ResultSet rs = statement.executeQuery();

或者您也可以使用此处所述的限制关键字http://docs.oracle.com/javadb/10.10.1.2/ref/rrefjdbclimit.html,您的查询将类似于

String query = "select * from TABLENAME { LIMIT 1000 OFFSET %d }";
String.format(query, firstrow);

答案 1 :(得分:4)

在Oracle中实现分页的常规方法是使用分析窗口函数,例如: row_number以及定义行排序的ORDER BY子句。然后将带有分析函数的查询包装到内联视图(或“窗口”)中,您可以从中查询所需的行号。这是一个查询my_table前1000行(按column_to_sort_by排序)的示例:

select rs.* from
  (select t.*,
    row_number() over (order by column_to_sort_by) as row_num
  from my_table t
  ) rs
where rs.row_num >= 1 and rs.row_num  < 1001
order by rs.row_num

JDBC实现可能如下所示:

public void queryWithPagination() throws SQLException {
    String query = "select rs.* from"
        + " (select t.*,"
        + " row_number() over (order by column_to_sort_by) as row_num"
        + " from my_table t"
        + " ) rs"
        + " where rs.row_num >= ? and rs.row_num  < ?"
        + " order by rs.row_num";

    final int pageSize = 1000;
    int rowIndex = 1;

    try (PreparedStatement ps = myConnection.prepareStatement(query)) {

      do {
         ps.setInt(1, rowIndex);
         ps.setInt(2, rowIndex + pageSize);
         rowIndex += pageSize;
      } while (handleResultSet(ps, pageSize));
    }
}

private boolean handleResultSet(PreparedStatement ps, final int pageSize)
        throws SQLException {
    int rows = 0;
    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            /*
             * handle rows here
             */
            rows++;
        }
    }
    return rows == pageSize;
}

请注意,在您阅读表时,该表应保持不变,以便分页在不同的查询执行中正常工作。

如果表中有这么多行你的内存不足,你可能需要在读完一些页面后清除/序列化你的列表。

修改

如果行的排序对你来说无关紧要,那么 - 正如@bdrx在他的回答中提到的那样 - 你可能不需要分页,而最快的解决办法是在没有{的情况下查询表。 {1}}中的{1}}条件。如建议的那样,您可以将语句的提取大小调整为更大的值以提高吞吐量。