如何在 Spring 或 Spring Boot 中使用键集分页
创始人
2024-03-18 00:28:27
0

介绍

在本文中,我将向您展示如何在 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, CustomPostRepository { }

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

外观如下:CustomPostRepository

1 2 3 4 5 6 7 8 9 10 11 12 public interface CustomPostRepository {       PagedList findTopN(         Sort sortBy,         int pageSize     );       PagedList findNextN(         Sort orderBy,         PagedList 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 findTopN(             Sort sortBy,             int pageSize) {         return sortedCriteriaBuilder(sortBy)             .page(0, pageSize)             .withKeysetExtraction(true)             .getResultList();     }       @Override     public PagedList findNextN(             Sort sortBy,             PagedList previousPage) {         return sortedCriteriaBuilder(sortBy)             .page(                 previousPage.getKeysetPage(),                 previousPage.getPage() * previousPage.getMaxResults(),                 previousPage.getMaxResults()             )             .getResultList();     }       private CriteriaBuilder sortedCriteriaBuilder(             Sort sortBy) {         CriteriaBuilder 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 firstLatestPosts(             int pageSize) {         return postRepository.findTopN(             Sort.by(                 Post_.CREATED_ON             ).descending().and(                 Sort.by(                     Post_.ID                 ).descending()             ),             pageSize         );     }       public PagedList findNextLatestPosts(             PagedList 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 topPage = forumService.firstLatestPosts(PAGE_SIZE);   assertEquals(POST_COUNT, topPage.getTotalSize());   assertEquals(POST_COUNT / PAGE_SIZE, topPage.getTotalPages());   assertEquals(1, topPage.getPage());   List 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 nextPage = forumService.findNextLatestPosts(topPage);   assertEquals(2, nextPage.getPage());   List 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 数据存储库轻松地自己实现它。

 

相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...