我想获取分页数据(从数据库)并将其调整为延迟加载的 Java Stream,以便我的 DAO 的客户端不需要担心页面索引、偏移量等。
我的设置如下:我有一个 DAO 方法,可以从数据库中获取数据页。具体来说,它使用MySQL SEEK来进行分页。即,要获取第 5 页,您需要向该方法提供第 4 页的最后一条记录。
使这种分页风格适应 Java Stream 的最简单方法是什么?对于我的用例来说,库很好,但框架(Spring、Androidx 等)则不然。
我自己编写的实现如下。 (
Field
、Record
和Result
是JOOQ课程)
public abstract class PagedSeekIterator<K> implements Iterator<Record> {
private K lastId = null;
private boolean done = false;
private Iterator<Record> currentIterator = null;
private final Field<K> idField;
public PagedSeekIterator(Field<K> idField) {
this.idField = idField;
}
@Override
public boolean hasNext() {
if (done) {
return false;
}
try {
if (currentIterator == null) {
// first call. Do init
currentIterator = fetchWithSeek(lastId).iterator();
return hasNext();
}
if (currentIterator.hasNext()) {
return true;
} else {
// current page is read through. Fetch next page
Result<Record> res = fetchWithSeek(lastId);
if (res.isEmpty()) {
// we're done here
done = true;
currentIterator = null;
return false;
} else {
// next page looks good
currentIterator = res.iterator();
return true;
}
}
} catch (SQLException e) {
throw new SqlRuntimeException("Failed to fetch page", e);
}
}
@Override
public Record next() {
if (!hasNext()) {
throw new NoSuchElementException("We're done here.");
}
Record rec = currentIterator.next();
lastId = rec.get(idField);
return rec;
}
// lastId will be null on the first call
protected abstract Result<Record> fetchWithSeek(K lastId) throws SQLException;
}
然后 Guava 可以方便地处理适应流:
Streams.stream(new PagedSeekIterator<Long>(Actions.ACTION.ID){
// override fetchWithSeek()
});
// returns a lazy Stream<Record>
我的实现有效,但我忍不住觉得我在这里重新发明轮子。
我也尝试过研究 AndroidX Paging,但是在 Android/Kotlin 上下文之外使用它是很痛苦的,而且我发现它增加了比我的普通 Java 项目减轻的复杂性更多的复杂性。
这是我的图书馆abacus-jdbc的解决方案。
如果您想通过一次查询返回包含所有记录(页面)的
stream
并按流API拆分:
JdbcUtil.prepareQuery(dataSource, sqlQuery)
//.setParameters(...); // if needed
stream(YourEntityClass.class); //.split(pageSize)
如果您想返回某个特定页面的
stream
(通过查找?)
final String query = "select * from yourTable where someIdColumn > ? order by someIdColumn limit pageSize";
JdbcUtil.prepareQuery(dataSource, query)
.setLong(1, lastPageEndId);
stream(YourEntityClass.class); // or stream(rowMapper);
如果您想退货
stream
。流中的每个元素都是一个 page
(记录列表),通过执行一次查询来加载:
final String query = "select * from yourTable where someIdColumn > ? order by someIdColumn limit pageSize";
JdbcUtil.queryByPage(dataSource, query, pageSize, (stmt, ret) -> stmt.setLong(1, ret == null ? startId : ret.get(ret.size() - 1).getId());