如何在 Spring 或 Spring Boot 中使用键集分页

介绍

在本文中,我将向您展示如何在 Spring 或 Spring Boot 中使用键集分页技术。

虽然 Spring DataPagingAndSortingRepository提供的基于偏移量的默认分页在许多情况下很有用,但如果您必须迭代大型结果集,那么键集分页或查找方法技术可以提供更好的性能。

什么是键集分页

本文所述,键集分页或查找方法允许我们在查找要加载的给定页面的第一个元素时使用索引。

加载最新 25 个实体的 Top-N 键集分页查询如下所示:Post

1
2
3
4
5
6
7
8
9
10
SELECT
    id,
    title,.8);">    created_on
FROM
    post
ORDER BY
    created_on DESC,.8);">    id DESC
FETCH FIRST 25 ROWS ONLY

加载第二、第三或第 n 页的 Next-N 查询如下所示:

10
11
12
13
WHERE
  (created_on,id) <
  (:previousCreatedOn,:previousId)
如您所见,Keyset 分页查询是特定于数据库的,因此我们需要一个框架,该框架可以为我们提供抽象此功能的 API,同时为每个受支持的关系数据库生成适当的 SQL 查询。

该框架称为Blaze Persistence,它支持JPA实体查询的Keyset Pagination

如何在 Spring 中使用键集分页

使用 Spring 时,数据访问逻辑是使用 Spring 数据存储库实现的。因此,基本数据访问方法由 定义,并且自定义逻辑可以在一个或多个自定义 Spring 数据存储库类中抽象。JpaRepository

这是实体数据访问对象,它看起来像这样:PostRepositoryPost

4
@Repository
public interface PostRepository
        extends JpaRepository<Post,Long>,CustomPostRepository {
}

本文所述,如果我们想提供额外的数据访问方法,我们可以在定义自定义数据访问逻辑的地方进行扩展。PostRepositoryCustomPostRepository

外观如下:CustomPostRepository

12
public interface CustomPostRepository {
 
    PagedList<Post> findTopN(
        Sort sortBy,.8);">        int pageSize
    );
    PagedList<Post> findNextN(
        Sort orderBy,.8);">        PagedList<Post> previousPage
实现接口的类如下所示:CustomPostRepositoryImplCustomPostRepository

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class CustomPostRepositoryImpl
        implements CustomPostRepository {
    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private CriteriaBuilderFactory criteriaBuilderFactory;
    @Override
    public PagedList<Post> findTopN(
            Sort sortBy,.8);">            int pageSize) {
        return sortedCriteriaBuilder(sortBy)
            .page(0,pageSize)
            .withKeysetExtraction(true)
            .getResultList();
    }
    public PagedList<Post> findNextN(
            PagedList<Post> previousPage) {
            .page(
                previousPage.getKeysetPage(),.8);">                previousPage.getPage() * previousPage.getMaxResults(),.8);">                previousPage.getMaxResults()
            )
    private CriteriaBuilder<Post> sortedCriteriaBuilder(
            Sort sortBy) {
        CriteriaBuilder<Post> criteriaBuilder = criteriaBuilderFactory
            .create(entityManager,Post.class);
             
        sortBy.forEach(order -> {
            criteriaBuilder.orderBy(
                order.getProperty(),.8);">                order.isAscending()
            );
        });
         
        return criteriaBuilder;
使用键集分页方法,如下所示:ForumServicePostRepository

35
@Service
@Transactional(readOnly = true)
public class ForumService {
    private PostRepository postRepository;
    public PagedList<Post> firstLatestPosts(
        return postRepository.findTopN(
            Sort.by(
                Post_.CREATED_ON
            ).descending().and(
                Sort.by(
                    Post_.ID
                ).descending()
            ),.8);">            pageSize
        );
    public PagedList<Post> findNextLatestPosts(
        return postRepository.findNextN(
            previousPage
}

测试时间

假设我们创建了 50 个实体:Post

19
LocalDateTime timestamp = LocalDateTime.of(
    2021123000
);
LongStream.rangeClosed(1,POST_COUNT).forEach(postId -> {
    Post post = new Post()
    .setId(postId)
    .setTitle(
        String.format(
            "High-Performance Java Persistence - Chapter %d",.8);">            postId
        )
    )
    .setCreatedOn(
        Timestamp.valueOf(timestamp.plusMinutes(postId))
    entityManager.persist(post);
});

加载第一页时,我们得到预期的结果:

PagedList<Post> topPage = forumService.firstLatestPosts(PAGE_SIZE);
assertEquals(POST_COUNT,topPage.getTotalSize());
assertEquals(POST_COUNT / PAGE_SIZE,topPage.getTotalPages());
assertEquals(1,topPage.getPage());
List<Long> topIds = topPage.stream().map(Post::getId).toList();
     
assertEquals(Long.valueOf(50),topIds.get(0));
assertEquals(Long.valueOf(49),topIds.get(1));

而且,在PostgreSQL上执行的SQL查询如下所示:

17
    p.id AS col_0_0_,.8);">    p.created_on AS col_1_0_,.8);">    p.id AS col_2_0_,.8);">    (
        SELECT count(*)
        FROM post post1_
    AS col_3_0_,.8);">    p.id AS id1_0_,.8);">    p.created_on AS created_2_0_,.8);">    p.title AS title3_0_
    post p
    p.created_on DESC,.8);">    p.id DESC
LIMIT 25

加载第二页时,我们得到下一个最新的 25 个实体:Post

8
PagedList<Post> nextPage = forumService.findNextLatestPosts(topPage);
assertEquals(2,nextPage.getPage());
List<Long> nextIds = nextPage.stream().map(Post::getId).toList();
assertEquals(Long.valueOf(25),nextIds.get(0));
assertEquals(Long.valueOf(24),nextIds.get(1));

底层 SQL 查询如下所示:

20
    (p.created_on,p.id) <
    ('2021-12-30 12:26:00.0',26) AND 0=0
很酷,对吧?

结论

键集分页在实现无限滚动解决方案时非常有用,虽然 Spring Data 中没有内置支持它,但我们可以使用 Blaze Persistence 和自定义 Spring 数据存储库轻松地自己实现它。

 

原文地址:https://blog.csdn.net/allway2

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要介绍了spring的事务传播属性REQUIRED_NESTED的原理介绍,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。传统事务中回滚点的使...
今天小编给大家分享的是一文解析spring中事务的传播机制,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获...
这篇文章主要介绍了SpringCloudAlibaba和SpringCloud有什么区别,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。Spring Cloud Netfli...
本篇文章和大家了解一下SpringCloud整合XXL-Job的几个步骤。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。第一步:整合pom文件,在S...
本篇文章和大家了解一下Spring延迟初始化会遇到什么问题。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。List 坑列表 = new ArrayList(2);...
这篇文章主要介绍了怎么使用Spring提供的不同缓存注解实现缓存的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇...
本篇内容主要讲解“Spring中的@Autowired和@Resource注解怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学...
今天小编给大家分享一下SpringSecurity怎么定义多个过滤器链的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家
这篇文章主要介绍“Spring的@Conditional注解怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring的@Con...
这篇文章主要介绍了SpringCloudGateway的熔断限流怎么配置的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇SpringCloud&nb...
今天小编给大家分享一下怎么使用Spring解决循环依赖问题的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考
这篇文章主要介绍“Spring事务及传播机制的原理及应用方法是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Sp...
这篇“SpringCloudAlibaba框架实例应用分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价
本篇内容主要讲解“SpringBoot中怎么使用SpringMVC”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习...
这篇文章主要介绍“SpringMVC适配器模式作用范围是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringMVC
这篇“导入SpringCloud依赖失败如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家...
这篇文章主要讲解了“SpringMVC核心DispatcherServlet处理流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来
今天小编给大家分享一下SpringMVCHttpMessageConverter消息转换器怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以...
这篇文章主要介绍“Spring框架实现依赖注入的原理是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring框架...
本篇内容介绍了“Spring单元测试控制Bean注入的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下