6

我经常看到这种模式。

在服务器上:

// 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);

在客户端:

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

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

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

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

4

2 回答 2

2

这是对我有用的东西。它还使用来自 google-guava 库的 AbstractIterator,但利用 Java8 Stream 来简化实现。它返回类型为 T 的元素的迭代器。

Iterator<List<T>> pagingIterator = new AbstractIterator<List<T>>() {
    private String resumeToken;
    private boolean endOfData;

    @Override
    protected List<T> computeNext() {
        if (endOfData) {
            return endOfData();
        }

        List<T> rows = executeQuery(resumeToken, PAGE_SIZE);

        if (rows.isEmpty()) {
            return endOfData();
        } else if (rows.size() < PAGE_SIZE) {
            endOfData = true;
        } else {
            resumeToken = getResumeToken(rows.get(PAGE_SIZE - 1));
        }

        return rows;
    }
};

// flatten Iterator of lists to a stream of single elements
Stream<T> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(pagingIterator, 0), false)
    .flatMap(List::stream);

// convert stream to Iterator<T>
return stream.iterator();

也可以通过以下方式使用方法引用返回 Iterable:

// convert stream to Iterable<T>
return stream::iterator;
于 2017-11-05T19:31:11.967 回答
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();
            }
        }};
}

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

于 2012-09-24T08:41:02.763 回答