分页查询/迭代器配方

ash*_*shm 6 java database pagination

我看到了很多这种模式.

在服务器上:

// Get a bounded number of results, along with a resume token to use 
// for the next call. Successive calls yield a "weakly consistent" view of 
// the underlying set that may or may not reflect concurrent updates.
public<T> String getObjects(
        int maxObjects, String resumeToken, List<T> objectsToReturn);
Run Code Online (Sandbox Code Playgroud)

在客户端:

// An iterator wrapping repeated calls to getObjects(bufferSize, ...)
public<T> Iterator<T> getIterator(int bufferSize);
Run Code Online (Sandbox Code Playgroud)

大多数地方都推出了这两种方法的自己版本,并且实现起来非常困难.有很多边缘案例错误.

这些查询是否有规范的配方或库?

(您可以对服务器端存储进行一些简化假设,例如T具有自然顺序).

Abh*_*kar 1

这是使用AbstractIteratorgoogle-guava 库和 spring-jdbc 来实际查询数据库的一个:

public Iterable<T> queryInBatches(
        final String query,
        final Map<String, Integer> paramMap,
        final int pageSize, final Class<T> elementType) {
    return new Iterable<T>() {
        @Override
        public Iterator<T> iterator() {
            final Iterator<List<T>> resultIter = 
                    queryResultIterator(query, paramMap, pageSize, elementType);

            return new AbstractIterator<T>() {
                private Iterator<T> rowSet;

                @Override
                protected T computeNext() {
                    if (rowSet == null) {
                        if (resultIter.hasNext()) {
                            rowSet = resultIter.next().iterator();
                        } else {
                            return endOfData();
                        }
                    }

                    if (rowSet.hasNext()) {
                        return rowSet.next();
                    } else {
                        rowSet = null;
                        return computeNext();
                    }
                }};
        }};
}


private AbstractIterator<List<T>> queryResultIterator(
        final String query, final Map<String, Integer> paramMap, 
        final int pageSize, final Class<T> elementType) {
    return new AbstractIterator<List<T>>() {
        private int page = 0;

        @Override
        protected List<T> computeNext() {
            String sql = String.format(
                    "%s limit %s offset %s", query, pageSize, page++ * pageSize);
            List<T> results = jdbc().queryForList(sql, paramMap, elementType);
            if (!results.isEmpty()) {
                return results;
            } else {
                return endOfData();
            }
        }};
}
Run Code Online (Sandbox Code Playgroud)

AbstractIterator隐藏了涉及编写您自己的Iterator. 您只需要实现computeNext返回迭代器中的下一个值或调用endOfData以指示迭代器中没有更多值的方法。