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

ほとんどの場所では、これら 2 つのメソッドの独自のバージョンが展開されており、実装を正しく行うのは驚くほど困難です。エッジケースのバグがたくさんあります。

これらのクエリの正規のレシピまたはライブラリはありますか?

(たとえば、T には自然な順序があるなど、サーバー側のストレージについていくつかの単純化した仮定を行うことができます)。

4

2 に答える 2

2

ここに私のために働くものがあります。google-guava ライブラリの AbstractIterator も使用しますが、Java8 Stream を利用して実装を簡素化します。T 型の要素の Iterator を返します。

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

これはAbstractIterator、google-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の独自の実装を記述することに伴う複雑さのほとんどを隠しますIteratorcomputeNextイテレータで次の値を返すか、イテレータendOfDataにそれ以上値がないことを示すために呼び出すメソッドのみを実装する必要があります。

于 2012-09-24T08:41:02.763 に答える