为什么使用分页查询?
当检索的数据量超过客户端的处理能力时,客户端就需要使用分页查询。对于portal要展示数据量超过千这个数量级时可能就需要分页查询。从数据库查询数据,符合条件的数据可能超过客户端单次处理能力(内存限制),这种情况也需要使用分页查询。
分页查询对于开发者友好吗?
分页查询可以有效的降低客户端和服务器的压力,降低数据传输占用的带宽。开发者使用分页查询的场景往往是想获取所有的数据并遍历数据做一些业务处理,但是分页查询增加了操作的难度,这明显对开发者不够友好。
包装分页查询
以华为云IoT提供的查询设备列表的API为例说明如何包装分页查询以方便使用。Java提供的Iterator可以完美的包装分页查询API以简化开发者对API的使用,实现思路:首先调用一次分页查询API,假设返回数据量为50,客户端使用next方法不断的获取数据并处理,当第一次分页查询返回的数据消费完时,触发第二次分页查询,直到所有的数据被处理完。下面是一个简单的样例代码:
private static class LazyIterator implements Iterator {
private String appId;
private String projectId;
private String token;
private List currentList = new ArrayList<>(50);
private int pos;
private String marker;
private long totalCount = Long.MAX_VALUE;
private long alreadyCount = 0L;
LazyIterator(String appId, String projectId, String token) {
this.appId = appId;
this.projectId = projectId;
this.token = token;
}
@Override
public boolean hasNext() {
return alreadyCount < totalCount;
}
@Override
public Device next() {
if (!currentList.isEmpty() && pos < currentList.size()) {
alreadyCount++;
return currentList.get(pos++);
} else {
Request request = createRequest();
SelfResponse response = HttpsClient.invoke(request, DevicesDTO.class);
PageDTO pageDTO = response.getResult().getPage();
this.marker = pageDTO.getMarker();
if (totalCount == Long.MAX_VALUE) {
this.totalCount = pageDTO.getCount();
}
List devices = response.getResult().getDevices();
currentList.clear();
pos = 0;
devices.forEach(deviceDTO -> {
Device device = new Device();
device.setDeviceId(deviceDTO.getDeviceId());
device.setNodeId(deviceDTO.getNodeId());
device.setAppId(deviceDTO.getAppId());
device.setProductId(deviceDTO.getProductId());
currentList.add(device);
});
}
alreadyCount++;
return currentList.get(pos++);
}
private Request createRequest() {
String url1 = "/v5/iot/{project_id}/devices";
String url = new HttpUrl.Builder()
.endpoint(ApplicationConf.getEndpoint())
.url(url1)
.replacePathVar(projectId)
.build().getUrl();
url = url + "?limit=50";
if (!StringUtil.isEmpty(appId)) {
url = url + "&app_id=" + appId;
}
if (!StringUtil.isEmpty(marker)) {
url = url + "&marker=" + marker;
}
return new Request.Builder()
.url(url)
.get()
.addHeader("X-Auth-Token", token)
.build();
}
}