Spring Webflux

Spring Webflux + r2dbc 分页查询 示例2

2020-06-08  本文已影响0人  这个世界是虚拟的

1. 现有API 对offset 的支持

我们可以看到, 如果使用R2dbc 已经实现的分页, 那么我们可以使用Pageable 进行分页查询, 但是如果我们需要使用offset 此时, 现有的支持也许并不够用, 那么我们需要自己来实现 Pageable 接口来实现offset分页

2. 为什么要使用offset 来进行分页

由于项目需要, 决策层决定只给API调用者提供获取下一页的功能, 而不是提供给API 调用者提供直接查询某一页的功能, 我个人理解是, 这样做可以直接让API调用方调用指定的需要的查询记录的位置而不是必须要页首.

3. 具体实现

@Value
public class Offset implements Pageable {

    int offset;
    int limit;
    //避免重用 isPaged
    boolean pagingApplied;

    @Override
    public int getPageNumber() {
        return offset / limit;
    }

    @Override
    public int getPageSize() {
        return limit;
    }

    @Override
    public long getOffset() {
        return offset;
    }

    @Override
    public Sort getSort() {
        //这里暂时不进行排序
        return Sort.unsorted();
    }

    @Override
    public Pageable next() {
        if (!pagingApplied){
            return unpaged();
        }
        return new Offset(offset + limit, limit, true);
    }

    @Override
    public Pageable previousOrFirst() {
        if (!pagingApplied){
            return unpaged();
        }
        //这里都返回 offset对象
        return hasPrevious() ? new Offset(offset - limit, limit, true) : first();
    }

    @Override
    public Pageable first() {
        if (!pagingApplied){
            return unpaged();
        }
        return new Offset(0, limit, true);
    }

    @Override
    public boolean hasPrevious() {
        return offset >= limit;
    }

    public static Offset unpaged() {
        return new Offset(0, 0, false);
    }

    @Override
    public boolean isPaged() {
        return pagingApplied;
    }
}

这里我们就已经实现了所有需要的方法, 那么我们可以使用我们的实现来使用 Offset 类来进行分页查询, 这里笔者觉得如果Spring Pageable 提供泛型会更加灵活, 这样的话我们直接可以指定返回类型, 不过也有可能Spring 本身有其他考量在此.

4. 使用Offset 类

4.1 使用方法和之前的使用方法一致, 那么我们如果定义了一个Repository 接口

MyRepository extends ReactiveCrudRepository<Employee, UUID>{
...
 Flux<Employee> getEmployeebyGender((@Param("gender") String gender, Pageable pageable)
...
}

4.2 调用

... ...
myRepository.getEmployeebyGender("man", new Offset(8, 10, true) )

4.3 构造函数

public Offset(int offset, int limit, boolean pagingApplied) {
        this.offset = Math.max(offset, 0);
        this.limit = Math.max(limit, 0);
        this.pagingApplied = pagingApplied;
    }

    public Offset(int offset, int limit) {
        this(offset, limit, true);
   }

但是这里还是有缺陷的, 如果外部调用构造函数, 例如 new Offset(5, 10, false), 那么势必,我们使用 pageable.isPaged() 会得到false, 那么我们接下来很可能会碰到一些问题, 但很显然我们需要使用new Offset(5, 10, true). 那么这里最后将构造函数私有, 然后提供,比如一个of的静态方法来让外部使用:

public static Offset of(int offset, Integer limit) {
//加入一些判断逻辑
        return new Offset(offset, limit, true);
    }

5. 遗留的问题

其实这里我们依然有一些问题存在, 例如, 如果控制层调用时, 提供的参数为空的情况, 我们就比较被动了, 以为我们使用的是int 型, 不允许为null, 所以有一种情况, 假设用户请求为: GET "/dosomting?limit=10", 此时, 我们获得的offset是空, 那么我们可能想设定offset = 0, 那么如果使用int 就办不到了, 所以我们需要 offset 为 Integer, 或者给offset 一个默认值等.

6. 遗留的问题

由于时间有限, 笔者没有进行过多的测试, 可能实现类中还有隐含问题待解决, 不过自己实现接口往往会遇到各种问题, 这也是很难避免的, 一般建议, 如果依赖包本身含有现成的实现类, 那么我们最好不要重复造轮子, 但是如果必须自己实现, 那良好的测试是必要的

上一篇下一篇

猜你喜欢

热点阅读