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

news2024/11/15 6:25:44

介绍

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

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

什么是键集分页

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT
    id,
    title,
    created_on
FROM
    post
WHERE
  (created_on, id) <
  (:previousCreatedOn, :previousId)
ORDER BY
    created_on DESC,
    id DESC
FETCH FIRST 25 ROWS ONLY

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

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

如何在 Spring 中使用键集分页

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

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

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

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

外观如下:CustomPostRepository

1
2
3
4
5
6
7
8
9
10
11
12
public interface CustomPostRepository {
 
    PagedList<Post> findTopN(
        Sort sortBy,
        int pageSize
    );
 
    PagedList<Post> findNextN(
        Sort orderBy,
        PagedList<Post> previousPage
    );
}

实现接口的类如下所示:CustomPostRepositoryImplCustomPostRepository

1
2
3
4
5
6
7
8
9
10
11
12
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,
            int pageSize) {
        return sortedCriteriaBuilder(sortBy)
            .page(0, pageSize)
            .withKeysetExtraction(true)
            .getResultList();
    }
 
    @Override
    public PagedList<Post> findNextN(
            Sort sortBy,
            PagedList<Post> previousPage) {
        return sortedCriteriaBuilder(sortBy)
            .page(
                previousPage.getKeysetPage(),
                previousPage.getPage() * previousPage.getMaxResults(),
                previousPage.getMaxResults()
            )
            .getResultList();
    }
 
    private CriteriaBuilder<Post> sortedCriteriaBuilder(
            Sort sortBy) {
        CriteriaBuilder<Post> criteriaBuilder = criteriaBuilderFactory
            .create(entityManager, Post.class);
             
        sortBy.forEach(order -> {
            criteriaBuilder.orderBy(
                order.getProperty(),
                order.isAscending()
            );
        });
         
        return criteriaBuilder;
    }
}

使用键集分页方法,如下所示:ForumServicePostRepository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Service
@Transactional(readOnly = true)
public class ForumService {
 
    @Autowired
    private PostRepository postRepository;
 
    public PagedList<Post> firstLatestPosts(
            int pageSize) {
        return postRepository.findTopN(
            Sort.by(
                Post_.CREATED_ON
            ).descending().and(
                Sort.by(
                    Post_.ID
                ).descending()
            ),
            pageSize
        );
    }
 
    public PagedList<Post> findNextLatestPosts(
            PagedList<Post> previousPage) {
        return postRepository.findNextN(
            Sort.by(
                Post_.CREATED_ON
            ).descending().and(
                Sort.by(
                    Post_.ID
                ).descending()
            ),
            previousPage
        );
    }
}

测试时间

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
LocalDateTime timestamp = LocalDateTime.of(
    2021123012000
);
 
LongStream.rangeClosed(1, POST_COUNT).forEach(postId -> {
    Post post = new Post()
    .setId(postId)
    .setTitle(
        String.format(
            "High-Performance Java Persistence - Chapter %d",
            postId
        )
    )
    .setCreatedOn(
        Timestamp.valueOf(timestamp.plusMinutes(postId))
    );
 
    entityManager.persist(post);
});

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

1
2
3
4
5
6
7
8
9
10
11
12
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查询如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
    p.id AS col_0_0_,
    p.created_on AS col_1_0_,
    p.id AS col_2_0_,
    (
        SELECT count(*)
        FROM post post1_
    AS col_3_0_,
    p.id AS id1_0_,
    p.created_on AS created_2_0_,
    p.title AS title3_0_
FROM
    post p
ORDER BY
    p.created_on DESC,
    p.id DESC
LIMIT 25

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

1
2
3
4
5
6
7
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 查询如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT
    p.id AS col_0_0_,
    p.created_on AS col_1_0_,
    p.id AS col_2_0_,
    (
        SELECT count(*)
        FROM post post1_
    AS col_3_0_,
    p.id AS id1_0_,
    p.created_on AS created_2_0_,
    p.title AS title3_0_
FROM
    post p
WHERE
    (p.created_on, p.id) <
    ('2021-12-30 12:26:00.0', 26) AND 0=0
ORDER BY
    p.created_on DESC,
    p.id DESC
LIMIT 25

很酷,对吧?

结论

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61595.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用awk聚合和排序

用awk聚合和排序 文章目录用awk聚合和排序一、需求1.1 源文件格式1.2 需求二、用awk实现2.1 写法2.2 效果一、需求 1.1 源文件格式 一份csv文件&#xff08;默认逗号分隔&#xff09;一共五列&#xff0c;其中一列是用户名文件名&#xff1a;日志文件.csv type日记idusernam…

Android使用ListView,DrawerLayout实现简单注册功能界面

1.效果展示 2.实现 1.主页面activity_main.xml 主页面就是简单的几个TextView和EditText以及单选框组成的一个注册表单。 <?xml version"1.0" encoding"utf-8"?> <LinearLayoutxmlns:android"http://schemas.android.com/apk/res/andro…

[附源码]JAVA毕业设计口腔医院网站(系统+LW)

[附源码]JAVA毕业设计口腔医院网站&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

java通过lock实现同步锁

这里我们是一个卖票的演示代码 其实 同步锁 远不止一个synchronized 它本身有一个 加上锁 和释放锁的过程 为了 让我们更好的理解这个过程 JDK5之后 为我们提供了一个单独的锁工具 lock lock是一个接口 他提供了 synchronized 方法 和 更广泛的语句操作 lock方法 获得锁 unl…

【C语言】函数传参与指针理解

文章目录指针与变量注意指针的本质指针和变量的用法函数与传参传变量与传指针的区别传变量与传指针的时机指针与变量 大三&#xff0c;但是C语言。目标&#xff1a;高屋建瓴&#xff0c;深入浅出。 注意 所有人在最开始学C语言的时候&#xff0c;老师都会和你说指针指向一个…

[附源码]JAVA毕业设计课程答疑系统(系统+LW)

[附源码]JAVA毕业设计课程答疑系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

LLM.int8()——自适应混合精度量化方法

Paper地址&#xff1a;https://arxiv.org/abs/2208.07339 GitHub链接&#xff1a;GitHub - TimDettmers/bitsandbytes: 8-bit CUDA functions for PyTorch 随着模型参数规模的增加&#xff0c;大模型&#xff08;如GPT-3&#xff0c;OPT-175B等NLP稠密大模型&#xff09;的实际…

某验三代滑块流程分析

一、请求流程 slide-float.html 首先请求了个HTML文本jquery.js 拿回一个jQuery的jsgt.js 拿回gt.js 像是某验的网址信息register-slide?t1669432270469 一个请求、携带时间戳。返回challenge gt等信息gettype.php 获取验证码类型 携带gtfullpage.9.1.0.js 滑块js代码get.php …

【Java开发】 Spring 08 :访问 Web 资源( 借助 RestTemplate 或 WebClient )

web 资源就是运行在服务器上的资源&#xff0c;比如放到 web 下的页面 js 文件、图片、css等&#xff0c;web资源分为静态web资源和动态web资源两类&#xff0c;接下来访问的就是动态资源&#xff08;页面返回的数据是动态的&#xff0c;由后端程序产生&#xff09;&#xff0…

Rust权威指南之编写自动化测试

一. 简述 虽然Rust的类型系统为我们提供了相当多的安全保障&#xff0c;但是还是不足以防止所有的错误。因此&#xff0c;Rust在语言层面内置了编写测试代码、执行自动化测试任务的功能。 测试是一门复杂的技术&#xff0c;本章覆盖关于如何编写优秀测试的每一个细节&#xf…

[LeetCode周赛复盘] 第 322 场周赛20221204

[LeetCode周赛复盘] 第 322 场周赛20221204 一、本周周赛总结二、 [Easy] 6253. 回环句1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6254. 划分技能点相等的团队1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6255. 两个城市间路径的最小分数1. 题目描述2. 思路分析3. 代…

细粒度图像分类论文研读-2017

文章目录Higher-order Integration of Hierarchical Convolutional Activations for Fine-grained Visual Categorization(by end-to-end feature encoding)AbstractIntroduction关于核关于多尺度Kernelized convolutional activationsMatching kernel and polynomial predicto…

秒懂数据结构之Map _ Set ,竟如此简单

Map、Set 文章目录 前言一、Map、Set的初步理解二、Map、Set的CURD方法的实现三、Map、Set的遍历总结前言 Set和Map天然就是高效搜索/查找的语义在这里我为什么将这两个集合分别列举比较呢&#xff1f;希望通过我的这篇博客可以增进大家对Map和Set的认识&#xff01;一、Map、…

[附源码]Python计算机毕业设计Django汽车美容店管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

计算卫星高度角、方位角

最小二乘定权、电离层对流层改正&#xff0c;都需要卫星的高度角、方位角。本章将介绍求解完卫星的地固坐标系的位置后&#xff0c;如何求解卫星的高度角、方位角。 卫星位置求解请参考之前的博客&#xff1a;卫星位置解算原理与程序设计 参考书籍&#xff1a;黄丁发&#xff0…

读<算法图解><笔记摘录>

从很多途径当中,看到过这本书的知识点,是一本很有趣的算法入门书籍,最近花费了几天的时间将其阅读完,总想着总结一下这本书的算法知识点,分享给大家,也让自己掌握地更加踏实一点. 算法:一组完成任何任务的指令 算法这玩意,在保证满足条件,并且不浪费内存的情况下,要尽可能速度…

18.定位元素练习-淘宝网

注意&#xff1a; 如果一个盒子定位元素属性既有left又有right,则会执行left属性。 既有top又有bottom&#xff0c;会执行top <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compa…

五子棋游戏AI智能算法设计

五子棋游戏C语言AI智能算法设计 近来发现编制五子棋游戏很有趣&#xff0c;尤其是AI智能算法很烧脑。网上介绍有什么贪心算法&#xff0c;剪枝算法&#xff0c;博弈树算法等等&#xff0c;不一而足。 对于人机对战的电脑智能应子算法&#xff0c;参阅很多五子棋书籍棋谱和五…

有序Map集合:LinkedHashMap和TreeMap该如何选用

文章目录前言一、为什么HashMap是无序的二、LinkedHashMap如何保证有序性三、TreeMap的底层原理四、LinkedHashMap和TreeMap比较总结前言 为什么HashMap是无序的&#xff1f;有序的Map集合有哪些&#xff1f;LinkedHashMap和TreeMap都是有序的Map集合&#xff0c;他们有什么区…

智能优化算法期末复习(更新ing)

目录 一、GA遗传算法 二、ACO蚁群算法 三、PSO粒子群算法 四、SA模拟退火算法 五、ABC人工蜂群算法 六、综合 一、GA遗传算法 1.运算流程 2.遗传算法适应值分配策略&#xff08;基于目标函数的直接分配、基于排名的分配&#xff09; 3.遗传算法在二进制问题&#xff08;如0…