Spring Data JPA 分页教程
分页查询避免一次性加载过多信息至内存,Spring Data JPA 分页功能非常强大且简单。本文带你一起了解如何使用。
1. 分页实现
对数据库记录进行分页查询需要下面步骤:
- 获得包括分页请求信息的Pageable对象
- 把分页对象作为参数传给相应的repository 方法
下面看看如何获得分页请求对象。
2. 分页对象
我们可以通过两种方法获取分页对象:手工创建和使用Spring Data web support。下面先看如何手工创建。
2.1. 手动创建分页对象
业务类或组件需要对结果分页,一般有 Spring Data JPA repository负责分页查询,但其需要传入分页对象,因此我们需要创建分页对象作为其参数。请看下面代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
final class RepositoryTodoSearchService implements TodoSearchService {
private final TodoRepository repository;
@Autowired
public RepositoryTodoSearchService(TodoRepository repository) {
this.repository = repository;
}
@Transactional(readOnly = true)
@Override
public Page<TodoDTO> findBySearchTerm(String searchTerm) {
Pageable pageRequest = createPageRequest()
//Obtain search results by invoking the preferred repository method.
Page<Todo> searchResultPage = ...
return TodoMapper.mapEntityPageIntoDTOPage(pageRequest, searchResultPage);
}
private Pageable createPageRequest() {
//Create a new Pageable object here.
}
}
接着讲解如何实现私有方法createPageRequest:
- 方法1:
获得第一页,页大小为10.
private Pageable createPageRequest() {
return new PageRequest(0, 10);
}
- 方法2:
如果需要对查询结果按照title和description进行升序排序,获取第二页,页大小为10.
private Pageable createPageRequest() {
return new PageRequest(1, 10, Sort.Direction.ASC, "title", "description");
}
- 方法3:
查询结果需要按照description字段进行降序排序,再按照title进行升序排序,获取第二页,页大小为10.
private Pageable createPageRequest() {
return new PageRequest(1,
10,
new Sort(Sort.Direction.DESC, "description")
.and(new Sort(Sort.Direction.ASC, "title"));
);
2.2. Spring Data Web Support
下面看看如何通过Spring Data Web Support获取分页对象。首先需要在应用上下文配置类上使用@EnableSpringDataWebSupport注解启用Spring Data Web Support。示例代码如下:
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableJpaRepositories(basePackages = {
"com.dataz.springdata.jpa.todo"
})
@EnableTransactionManagement
@EnableSpringDataWebSupport
class PersistenceContext {
}
该注解注册了两个HandlerMethodArgumentResolver 对象,分别为:
-
SortHandlerMethodArgumentResolver 负责从请求中抽取排序信息。
-
PageableHandlerMethodArgumentResolver 负责从请求中抽取分页信息。
现在可以在执行查询是通过设定参数指定请求分页信息并配置排序选项:
-
page 请求参数指定请求页的页数,第一页是0,缺省值也是0。
-
size 请求参数指定请求的页大小,缺省值为20。
-
sort 请求参数指定查询排序选项。Spring Data JPA 参考文档描述内容为:“需要排序属性按照格式
property,property(,ASC|DESC)
描述。缺省为升序,可以对多个参数切换排序方式,示例:?sort=firstname&sort=lastname,asc
。”
启用了Spring Data web support之后,就可以在Controller中注入Pageable对象。请看示例代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
final class TodoSearchController {
private final TodoSearchService searchService;
@Autowired
public TodoSearchController(TodoSearchService searchService) {
this.searchService = searchService;
}
@RequestMapping(value = "/api/todo/search", method = RequestMethod.GET)
public Page<TodoDTO> findBySearchTerm(@RequestParam("searchTerm") String searchTerm,
Pageable pageRequest) {
return searchService.findBySearchTerm(searchTerm, pageRequest);
}
}
TodoSearchController 从TodoSearchService 对象中获取返回结果。RepositoryTodoSearchService 实现TodoSearchService 接口,findBySearchTerm()方法简单传递搜索关键字和分页对象给repository ,下面请看RepositoryTodoSearchService 代码:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
final class RepositoryTodoSearchService implements TodoSearchService {
private final TodoRepository repository;
@Autowired
public RepositoryTodoSearchService(TodoRepository repository) {
this.repository = repository;
}
@Transactional(readOnly = true)
@Override
public Page<TodoDTO> findBySearchTerm(String searchTerm, Pageable pageRequest) {
//Obtain search results by invoking the preferred repository method.
Page<Todo> searchResultPage = ...
return TodoMapper.mapEntityPageIntoDTOPage(pageRequest, searchResultPage);
}
}
3. Repository使用分页对象执行查询
已经获得分页对象之后,我们需要创建数据库查询执行分页查询。下面开始定义Repository。让我们的Repository继承PagingAndSortingRepository 接口:
import org.springframework.data.repository.PagingAndSortingRepository;
interface TodoRepository extends PagingAndSortingRepository<Todo, Long> {
}
PagingAndSortingRepository接口声明了一个方法findAll,负责对数据库查询进行分页。
- Page findAll(Pageable pageRequest) 方法根据传入分页对象返回分页查询结果。
如果你不想分页,则可以使用Iterable findAll() 代替Page findAll(Pageable pageRequest) 方法。我们还可以继承QuerydslPredicateExecutor接口,实现其findAll方法。
Page<T> findAll(Predicate predicate, Pageable pageable);
其中 Predicate predicate 也是Spring Data web support提供的功能,基于实体进行灵活的条件过滤,该主题不是本文重点,暂不展开,读者可以参考相关博文。
4. 总结
本文介绍了Spring Data JPA 分页方法,包括两种方式获取分页对象,以及如何定义Repository的分页方法执行分页查询。