我看到这种模式很多。
在服务器:
// 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有一个自然顺序)。
下面是一个使用AbstractIterator
从谷歌,番石榴库和弹簧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
指示有在迭代器没有更多的价值。
下面是一些对我的作品。 它还使用AbstractIterator从谷歌,番石榴库,但需要Java8流的优势,简化实现。 它返回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();
另外,也可以通过使用以下述方式方法参照返回一个可迭代:
// convert stream to Iterable<T>
return stream::iterator;