Web应用程序中的分页是将大结果集分成较小块的一种机制。提供流畅的分页导航器可以增加您的搜索引擎网站的价值,并通过最小化响应时间来增强用户体验。分页的着名例子是Google的搜索结果页面。通常,结果页面分为几页。为了避免之前提到的用户体验不佳,很多网站只显示当前,第一个,最后一个和一些相邻的页面。
要在Spring框架中实现分页,我们可以选择不同的选择。Spring框架提供了一个开箱即用的分页功能,需要页数和每页元素数量。当我们实现“静态分页”,这可以为用户提供不同页面之间的选择,这非常有用。
我们如何实现动态分页,当我们向下滚动时,它会自动加载页面的内容?在本文中,我将介绍静态和动态分页以及实现这些服务的不同方法。
静态分页与弹簧数据
这是在Web应用程序中实现分页的最方便的方法。它只需要获取页面和每页查询结果的数量。而不是使用Repository或CrudRepository,您应该使用PaginationAndSortingRepository,它接受“Pageable”类型的对象。
Pageable对象需要页面的编号和每页元素的数量。可分页对象的这些属性将在查询中返回不同的结果。结果是具有来自整个结果集的特定行的元素的“页面”。响应还包括您发送的特定查询的总元素的元数据,以及总页数。该元数据信息对于前端可能是有用的,并且可以构建包括前一页和下一页的链接的UI。在此链接中,您可以看到分页方法的结果。
如何实现静态分页
在这里,您可以找到存储库和服务层的示例代码,它从数据库中返回分页结果。如果您打开调试日志,无论是在application.properties还是Logback文件中,都可以看到由Spring Data创建的查询。
@Repository
公共 接口 SomethingRepository 扩展了 PaginationAndSortingRepository < Something,Long > {
@Query(“从东西选择s”
+ “加入s.somethingelse as se”
+ “其中se.id =:somethingelseid”)
页面< Something > findBySomethingElseId(@Param(“somethingelseid”)long somethingelseid,
分页 分页);
@服务
public class SomethingService {
private SomethingRepository somethingRepository ;
@Autowired
public SomethingService(SomethingRepository somethingRepository){
这个。somethingRepository = somethingRepository ;
}
@Transactional(readOnly = true)
public PageDto getSomething(long somethingElseId,int page,int size){
页面< Something > somethings = somethingRepository。findBySomethingElseId(somethingElseId,new PageResult(page,size));
返回 新 PageDto(出头。的getContent()
。stream()
。map(SomethingDto :: createDto)
。sorted(比较(SomethingDto :: getDatum))
。collect(toList()),某些东西。getTotalElements(),somethings。getTotalPages();
}
}
@Controller
// ....
使用本机查询进行动态分页
如前所述,另一种类型的分页是动态分页。这种分页可以在Spring框架中使用本地查询来完成,如下面的代码示例所示。对于这种类型,您需要指定一个偏移行和一个限制,这是该偏移量之后的元素数量。您还需要编写您的“页面”类,并将响应列表映射到它。
如何使用本地查询来实现动态分页
在这里,您可以找到存储库和服务层和数据传输对象(DTO),这些对象将用于映射我们的结果并将其发送到控制器层。
公共 接口 CustomSomethingRepository {
List < Something > findPagedResultBySomethingElseId(long somethingElseId,int offset,int limit);
}
public class SomethingRepositoryImpl 实现 CustomSomethingRepository {
@Autowired
私有 EntityManager em ;
@SuppressWarnings(“未选中”)
@覆盖
public List < Something > findPagedResultBySomethingElseId(long somethingElseId,int offset,int limit){
String query = “select s。* from Something”
+ “join somethingelse selse on selse.id = s.fk_somethingelse”
+ “where selse.id =:somethingElseId”
+ “order by selse.date” ;
查询 nativeQuery = em。createNativeQuery(query);
nativeQuery。setParameter(“somethingElseId”,somethingElseId);
// Paginering
nativeQuery。setFirstResult(offset);
nativeQuery。setMaxResults(limit);
final List < Object [] > resultList = nativeQuery。getResultList();
列表< Something > somethingList = 列表。newArrayList();
resultList。的forEach(对象 - > somethingList。添加(//地图OBJ的东西));
返回 somethingList ;
}
}
Hibernate将按如下方式翻译您的查询:
SELECT inner_query。*,ROW_NUMBER()OVER(ORDER BY CURRENT_TIMESTAMP)as __hibernate_row_nr__ FROM(select TOP(?)t as page0_ from Something s join s .somethingelse as selse order by selse .date)inner_query)SELECT page0_ FROM query WHERE __hibernate_row_nr__> = ?AND __hibernate_row_nr__ < ?
@服务
public class SomethingService {
private SomethingRepository somethingRepository ;
@Autowired
public SomethingService(SomethingRepository somethingRepository){
这个。somethingRepository = somethingRepository ;
}
@Transactional(readOnly = true)
public PageDto getSomething(long somethingElseId,int page,int size){
列表< Something > somethings = somethingRepository。findBySomethingElseId(somethingElseId,offset,limit);
返回 新的 PagedResult <>(有些东西
。stream()
。map(SomethingDto :: createDto)
。sorted(比较(SomethingDto :: getDatum))
。collect(toList()),某些东西。getTotalElements(),somethings。getTotalPages();
}
}
@Controller
// ....
公共 类 PagedResult < T > {
public static final long DEFAULT_OFFSET = 0 ;
public static final int DEFAULT_MAX_NO_OF_ROWS = 100 ;
private int offset ;
私人 INT 极限 ;
私人 长期 totalElements ;
private List < T > 元素 ;
public PagedResult(List < T > 元素,long totalElements,int offset,int limit){
这个。元素 = 元素 ;
这个。totalElements = totalElements ;
这个。offset = offset ;
这个。limit = limit ;
}
public boolean hasMore(){
return totalElements > offset + limit ;
}
public boolean hasPrevious(){
返回 偏移量 > 0 && totalElements > 0 ;
}
public long getTotalElements(){
返回 totalElements ;
}
public int getOffset(){
返回 偏移 ;
}
public int getLimit(){
返回 限制 ;
}
public List < T > getElements(){
返回 元素 ;
}
}
利弊
优点:与使用Spring数据相比,将生成更少的SQL查询。这些复杂的查询不能写在Spring Data中,我们必须将我们的查询指定为本机的查询,这仍然可以使用这种方法进行分页。
缺点:“对象”数组必须映射到Java对象。这是痛苦和难以维护。
如何使用Spring数据实现OffsetLimit分页
据我所知,在默认的Spring数据存储库中,您不需要“开箱即用”的支持。但是您可以创建一个可以使用限制/偏移参数的Pageable对象的自定义实现。
创建一个可分页的对象并将其传递给PaginationAndSortingRepository:
public class OffsetLimitRequest implements Pageable {
私人 INT 极限 ;
private int offset ;
public OffsetLimitRequest(int offset,int limit){
这个。limit = limit ;
这个。offset = offset ;
}
@覆盖
public int getPageNumber(){
返回 0 ;
}
@覆盖
public int getPageSize(){
返回 限制 ;
}
@覆盖
public int getOffset(){
返回 偏移 ;
}
....
}
这意味着不需要更改存储库层。您唯一需要改变的就是做到服务层,如下所示:
@服务
public class SomethingService {
private SomethingRepository somethingRepository ;
@Autowired
public SomethingService(SomethingRepository somethingRepository){
这个。somethingRepository = somethingRepository ;
}
@Transactional(readOnly = true)
public PageDto getSomething(long somethingElseId,int page,int size){
页面< Something > somethings = somethingRepository。findBySomethingElseId(somethingElseId,new OffsetLimitRequest(offset,limit));
返回 新 PageDto(出头。的getContent()
。stream()
。map(SomethingDto :: createDto)
。sorted(比较(SomethingDto :: getDatum))
。collect(toList()),某些东西。getTotalElements(),somethings。getTotalPages();
}
}
请注意,您不需要手动映射结果,并且将从开发中花费大量时间。
对结果进行排序
您可以在上面的代码片段中看到,您可以使用Java 8中的Stream接口基于属性对结果进行排序。您还可以使用“org.springframework.data”包中的“sort”对象参数来开发可分页对象。域”。可以使用ASC或DESC的方向以及实体的属性名称的可迭代对象来启动排序对象。我们之前的OffsetLimitRequest类更改如下:
public class OffsetLimitRequest implements Pageable,Serializable {
private static final long serialVersionUID = - 4541509938956089562L ;
私人 INT 极限 ;
private int offset ;
私有 排序 排序 ;
public OffsetLimitRequest(int offset,int limit,Sort sort){
这个。limit = limit ;
这个。offset = offset ;
这个。排序 = 排序 ;
}
@覆盖
public int getPageNumber(){
返回 0 ;
}
@覆盖
public int getPageSize(){
返回 限制 ;
}
@覆盖
public int getOffset(){
返回 偏移 ;
}
@覆盖
public Sort getSort(){
返回 排序 ;
}
....
}
哪些可以在我们的服务层启动:
@服务
public class SomethingService {
private SomethingRepository somethingRepository ;
@Autowired
public SomethingService(SomethingRepository somethingRepository){
这个。somethingRepository = somethingRepository ;
}
@Transactional(readOnly = true)
public PageDto getSomething(long somethingElseId,int page,int size){
列表< String > sortedOnSomethingsAttributes = 数组。asList(“attr1”,“attr2”);
页面< Something > somethings = somethingRepository。findBySomethingElseId(somethingElseId,新 OffsetLimitRequest(偏移,限制,新 排序(排序。方向。fromString(“ASC” ),sortingOnSomethingsAttributes));
返回 新 PageDto(出头。的getContent()
。stream()
。map(SomethingDto :: createDto)
。collect(toList()),某些东西。getTotalElements(),somethings。getTotalPages();
}
}
请注意,我们不需要在Stream的return语句中进行排序。当我们处理大的结果集时,最好在数据库附近设置排序功能以获得更好的性能。
本文暂时没有评论,来添加一个吧(●'◡'●)