【问题标题】:How to implement pagination by nextPageToken?如何通过 nextPageToken 实现分页?
【发布时间】:2020-06-19 15:35:50
【问题描述】:

我正在尝试使用 nextPageToken 实现分页。

我有桌子:

CREATE TABLE IF NOT EXISTS categories
(
    id        BIGINT PRIMARY KEY,
    name      VARCHAR(30) NOT NULL,
    parent_id BIGINT REFERENCES categories (id)
);

所以我有实体类别:

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;
    @Column(name = "name")
    private String name;
    @OneToOne
    @JoinColumn(name = "parent_id", referencedColumnName = "id")
    private Category category;

我真的不明白我接下来要做什么。 客户端请求令牌(保留什么?)

假设我有控制器:

@GetMapping
    public ResponseEntity<CategoriesTokenResponse> getCategories(
            @RequestParam String nextPageToken
    ) {
        return ResponseEntity.ok(categoryService.getCategories(nextPageToken));
    }

服务:

public CategoriesTokenResponse getCategories(String nextPageToken) {
        return new CategoriesTokenResponse(categoryDtoList, "myToken");
    }
@Data
@AllArgsConstructor
public class CategoriesTokenResponse {
    private final List<CategoryDto> categories;
    private final String token;
}

我必须如何为此实现 sql 查询?以及如何为每个 id 生成 nextPagetoken?

SELECT * FROM categories WHERE parent_id = what?
AND max(category id from previous page = token?)
ORDER BY id LIMIT 20;

【问题讨论】:

  • 你有没有想过为 REST 或 JPA 使用 spring boots 内置的分页功能?
  • 一个聪明人说,由于 OFFSET 和其他原因,它的实现不好,这是个坏主意,所以我尝试实现自定义分页
  • 我认为,如果您可以使用经过实战验证的解决方案,请使用它,除非您有证据证明它不适合您,即您已经对其进行了分析以发现此实现是,是什么扼杀了你的表现。否则,您很可能最终会在微优化上浪费金钱,并且可能甚至无法证明您已经优化了任何东西,因为缺少比较测量。
  • 不要听你所听到的一切。即使是我,现在也是。
  • @derM 所以 PagingAndSortingRepository 总是在生产中使用?我需要像现在这样的字段 parent_id 吗?

标签: java sql spring pagination token


【解决方案1】:

首先,您需要了解您在此处使用的内容。每个实现都有某种限制或效率低下。例如,使用这样的页面标记只适用于无限滚动页面。您不能跳转到任何特定页面。因此,如果我的浏览器崩溃并且我在第 100 页,我必须再次滚动浏览 100 页。对于海量数据集肯定会更快,但是如果您需要访问所有页面,这有关系吗?或者,如果您从一开始就限制回报?比如只获取前 30 页?

基本上决定这一点:您是否只关心前几页,因为搜索/排序总是在使用中? (就像用户从不使用超过 google 的前 1-5 页)并且该数据集很大吗?然后是很好的用例。用户会选择“过去 6 个月内的所有项目”并且实际上需要所有项目,还是排序/搜索弱?或者,您会返回所有页面而不限制最大返回 30 页吗?或者,开发速度是否比 0.1-3 秒(取决于数据大小)的速度增加更重要?然后使用内置的 JPA Page 对象。

我在 700k 记录上使用了 Page 对象,与 70k 记录相比,速度变化不到一秒。基于此,除非您计划拥有庞大的数据集,否则我认为删除偏移量不会增加大量价值。我刚刚测试了一个我正在使用可分页制作的新系统,它在 84 毫秒内返回第 1 页上的 10 个项目,没有页面限制器,将 vpn 上的 27k 记录从我家传到我的工作网络。包含超过 500k 条记录的表需要 131 毫秒,这非常快。想让它更快吗?强制总最大返回 30 页和每页最多 100 个结果,因为通常情况下,他们不需要该表中的所有数据。他们想要别的东西吗?细化搜索。这与搜索/键类型分页之间的速度差异小于一秒。这也是假设一个普通的 SQL 数据库。 NoSQL 在这里有点不同。 Baeldung 有大量关于 jpa 分页的文章,如下所示:https://www.baeldung.com/rest-api-pagination-in-spring

JPA Paging 学习和实施应该不超过 30 分钟,它非常简单,并且在 JPA 存储库中提供。我强烈建议在搜索/键样式分页上使用它,因为您可能没有构建像 google 或 facebook 这样的系统。

如果您绝对想使用 seek/key 样式的分页,这里有一个很好的信息页面: https://blog.jooq.org/2013/10/26/faster-sql-paging-with-jooq-using-the-seek-method/

一般来说,您正在寻找的是将 JOOQ 与 spring 一起使用。这里的实现示例: https://docs.spring.io/spring-boot/docs/1.3.5.RELEASE/reference/html/boot-features-jooq.html

基本上,创建一个 DSL 上下文:

private final DSLContext DSL;

@Autowired
public JooqExample(DSLContext dslContext) {
    this.DSL= dslContext;
}

然后像这样使用它:

DSL.using(configuration)
   .select(PLAYERS.PLAYER_ID,
           PLAYERS.FIRST_NAME,
           PLAYERS.LAST_NAME,
           PLAYERS.SCORE)
   .from(PLAYERS)
   .where(PLAYERS.GAME_ID.eq(42))
   .orderBy(PLAYERS.SCORE.desc(),
            PLAYERS.PLAYER_ID.asc())
   .seek(949, 15) // (!)
   .limit(10)
   .fetch();

不用显式地表达搜索谓词,只需传递上一个查询的最后一条记录,jOOQ 将看到在给定 ORDER BY 子句的情况下跳过该记录之前和包括该记录的所有记录。

【讨论】:

  • 谢谢,这个回答很有用!
  • 当我创建我的第一个 api 时,我对这个主题做了很多研究,因为它是用于一个每年可以获取 160 万条记录的系统。另外,我建议阅读链接标题。我讨厌容器,所以不是拥有一个保存对象数据的容器,而是在链接头中返回第一个、最后一个、下一个、上一个,而正文可以只保存对象数据。我也不使用hateoas,除非它也是一个大系统。除非您有大量的消费者或不断变化的系统,否则这没什么用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-05
  • 1970-01-01
  • 2017-07-04
相关资源
最近更新 更多