在本教程中,我将向您展示如何使用Spring Data JPA在Spring Boot中按多个列对结果进行排序/排序。并且介绍同时应用排序和分页的方法。
Spring 数据排序多列示例概述
假设我们在数据库中有教程表,如下所示:
以下是一些按单列/多列(带/不带分页)排序的 URL 示例,按升序或降序排序:
/api/tutorials
按 [id, 降序] 排序(默认)/api/tutorials?sort=title,asc
排序方式为 [标题,升序]/api/tutorials?sort=published,desc&sort=title,asc
按列 [已发布,降序]排序,然后按列 [标题,升序] 排序/api/tutorials?page=0&size=3&sort=published,desc&sort=title,asc
按列 [已发布、降序] 排序,然后按列 [标题、升序] 和分页排序
在本教程中,为了帮助您清楚地了解如何使用Spring Boot进行排序,我将创建具有不同响应结构的分离端点:
- 用于按多列排序:
/api/sortedtutorials
[
{
"id": 8,
"title": "Spring Data JPA Tut#8",
"description": "Tut#8 Description",
"published": true
},
...
...
...
{
"id": 1,
"title": "Spring Boot Tut#1",
"description": "Tut#1 Description",
"published": false
}
]
- 对于分页和排序组合在一起:
/api/tutorials
{
"totalItems": 8,
"tutorials": [
...
],
"totalPages": 3,
"currentPage": 1
}
请访问: 春靴分页和排序示例
让我们看一下构建此弹簧启动应用程序后的结果:
– 获取所有具有默认顺序 [id, 降序] 的教程:
– 获取所有教程,按单列排序 [标题,升序]:
– 获取所有教程,按多列排序 [已发布,降序] & [标题,升序]:
使用弹簧数据JPA按多个列排序
为了帮助我们处理这种情况,Spring Data JPA 提供了使用分页和排序存储库实现分页的方法。
PagingAndSortingRepository
扩展 Crud存储库以提供使用排序抽象检索实体的其他方法。因此,您可以向查询方法添加一个特殊的 Sort 参数。
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
}
findAll(Sort sort)
:返回满足对象提供的排序条件Sort
的实体的Iterable
。
您还可以使用Sort
其他参数定义更多派生查询方法和自定义查询方法。例如,以下方法返回标题包含给定字符串的教程列表:
List<Tutorial> findByTitleContaining(String title, Sort sort);
您可以在此处找到方法名称中更多受支持的关键字。
让我们继续探索Sort
类。
弹簧数据排序和排序
Sort 类为数据库查询提供排序选项,在选择单/多排序列和方向(升序/降序)时具有更大的灵活性。
例如,我们使用 by()
、descending()
、and()
方法来创建对象并将其传递给Sort
Repository.findAll()
:
// order by 'published' column - ascending
List<Tutorial> tutorials =
tutorialRepository.findAll(Sort.by("published"));
// order by 'published' column, descending
List<Tutorial> tutorials =
tutorialRepository.findAll(Sort.by("published").descending());
// order by 'published' column - descending, then order by 'title' - ascending
List<Tutorial> tutorials =
tutorialRepository.findAll(Sort.by("published").descending().and(Sort.by("title")));
我们还可以使用Order对象列表创建一个新Sort
对象
。
List<Order> orders = new ArrayList<Order>();
Order order1 = new Order(Sort.Direction.DESC, "published");
orders.add(order1);
Order order2 = new Order(Sort.Direction.ASC, "title");
orders.add(order2);
List<Tutorial> tutorials = tutorialRepository.findAll(Sort.by(orders));
分页和排序
如果我们想要对数据进行排序和分页,该怎么办?
CrudRepository
还提供了使用分页抽象检索实体的其他方法。
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Page<T> findAll(Pageable pageable);
}
findAll(Pageable pageable)
:返回满足Pageable
对象提供的分页条件的Page
实体。
Spring Data还支持从方法名称创建许多有用的查询,我们将在此示例中使用这些方法名称来过滤结果,例如:
Page<Tutorial> findByPublished(boolean published, Pageable pageable);
Page<Tutorial> findByTitleContaining(String title, Pageable pageable);
有关分页和排序的更多详细信息,请访问:
春靴分页和排序示例
弹簧启动应用
您可以逐步进行操作,也可以在本文中获取源代码:
弹簧启动,弹簧数据JPA - Rest CRUD API示例
Spring 项目包含的结构,我们只需要添加一些更改即可使分页正常工作。
或者,您可以在本教程结束时获取新的 Github 源代码(包括分页和排序)。
数据模型
这是我们将要处理的教程实体:
model/Tutorial.java
package com.bezkoder.spring.data.jpa.pagingsorting.model;
import javax.persistence.*;
@Entity
@Table(name = "tutorials")
public class Tutorial {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "title")
private String title;
@Column(name = "description")
private String description;
@Column(name = "published")
private boolean published;
public Tutorial() {
}
public Tutorial(String title, String description, boolean published) {
this.title = title;
this.description = description;
this.published = published;
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isPublished() {
return published;
}
public void setPublished(boolean isPublished) {
this.published = isPublished;
}
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
}
}
支持分页和排序的存储库
在本教程的早期,我们知道PagingAndSortingRepository
,但是在此示例中,为了保持连续性并利用Spring数据JPA,我们继续使用扩展PagingAndSortingRepository
接口的Jpa存储库。
repository/TutorialRepository.java
package com.bezkoder.spring.data.jpa.pagingsorting.repository;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bezkoder.spring.data.jpa.pagingsorting.model.Tutorial;
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
Page<Tutorial> findByPublished(boolean published, Pageable pageable);
Page<Tutorial> findByTitleContaining(String title, Pageable pageable);
List<Tutorial> findByTitleContaining(String title, Sort sort);
}
在上面的代码中,我们将添加pageable
参数与Spring查询创建一起使用,以查找标题包含输入字符串的所有教程。
更多派生查询,请参见:
弹簧启动中的 JPA 存储库查询示例
带注释的自定义查询:
春季 JPA @Query示例:春季启动中的自定义查询@Query
按多列排序/排序的控制器
要获取多个排序请求参数,我们使用默认参数defaultValue = "id,desc"的
@RequestParam String[] sort
。
在编写 Controller 方法来处理这种情况之前,让我们看看我们用参数检索了什么:
?sort=column1,direction1
:排序单列String[] sort
是一个包含 2 个元素的数组:[“列 1”、“方向 1”]?sort=column1,direction1&sort=column2,direction2
:对多列进行排序,String[] sort
也是一个包含 2 个元素的数组:[“列 1,方向 1”、“列 2,方向 2”]
这就是为什么我们需要检查数组中的第一项是否包含","
。
我们还需要转换/转化"asc"
"desc"
为Sort.Direction.ASC
/Sort.Direction.DES
与Sort.Order类
一起工作。
controller/TutorialController.java
package com.bezkoder.spring.data.jpa.pagingsorting.controller;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
...
import com.bezkoder.spring.data.jpa.pagingsorting.model.Tutorial;
import com.bezkoder.spring.data.jpa.pagingsorting.repository.TutorialRepository;
@RestController
@RequestMapping("/api")
public class TutorialController {
@Autowired
TutorialRepository tutorialRepository;
private Sort.Direction getSortDirection(String direction) {
if (direction.equals("asc")) {
return Sort.Direction.ASC;
} else if (direction.equals("desc")) {
return Sort.Direction.DESC;
}
return Sort.Direction.ASC;
}
@GetMapping("/sortedtutorials")
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(defaultValue = "id,desc") String[] sort) {
try {
List<Order> orders = new ArrayList<Order>();
if (sort[0].contains(",")) {
// will sort more than 2 columns
for (String sortOrder : sort) {
// sortOrder="column, direction"
String[] _sort = sortOrder.split(",");
orders.add(new Order(getSortDirection(_sort[1]), _sort[0]));
}
} else {
// sort=[column, direction]
orders.add(new Order(getSortDirection(sort[1]), sort[0]));
}
List<Tutorial> tutorials = tutorialRepository.findAll(Sort.by(orders));
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(tutorials, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
将分页和排序结合在一起的控制器怎么样?
请访问: 春靴分页和排序示例
结论
在这篇文章中,我们学习了如何使用弹簧数据JPA,排序类和可分页界面在弹簧启动应用程序中按多个列进行排序/排序。
我们还看到,这支持了一种无需样板代码即可制作排序、分页和筛选方法的好方法。JpaRepository
带注释的自定义查询:
春季 JPA @Query示例:春季启动中的自定义查询@Query
您还可以在本文中了解如何:
- 分页和过滤。
– 在这篇文章中处理异常。
– 通过本教程在 AWS 上部署此春季启动应用程序(免费)。
学习愉快!再见。
延伸阅读
对于分页和排序:
弹簧靴分页和排序示例
源码
您可以在 Github 上找到本教程的完整源代码。
更多派生查询,请参见:
弹簧启动中的 JPA 存储库查询示例
您可以将代码用作以下帖子的附加功能:
– 弹簧启动,弹簧数据 JPA,H2 示例
– 弹簧启动、弹簧数据 JPA、MySQL 示例
– 弹簧启动、弹簧数据 JPA、PostgreSQL 示例
– 弹簧启动、弹簧数据 JPA、SQL Server 示例
– 弹簧启动、弹簧数据 JPA、Oracle 示例
关联:
– 使用 JPA 的弹簧启动一对一示例,休眠
– 使用 JPA 的弹簧启动一对多示例,使用 JPA 的
弹簧启动多对多示例,休眠
单元测试:
– JPA 回收的弹簧启动单元测试
– 静止控制器的弹簧启动单元测试