当接口请求需要分页数据时,分页参数(如 page 和 size)通常不需要存储在实体中,而是通过 Spring Data JPA 提供的分页功能来实现。这涉及到对 Controller、 Service 和 Repository 的改造。以下是完整的实现说明和代码示例。
1. 使用 Pageable 和 Page 接口
Spring Data JPA 内置了分页支持,可以直接通过 Pageable 和 Page 来处理分页逻辑。
Controller 层
示例代码
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public Page<User> getUsers(
@PageableDefault(size = 10, page = 0) Pageable pageable) {
return userService.getUsers(pageable);
}
}
关键点
1. 分页参数的注解:
• 使用 @PageableDefault 提供默认分页参数,如每页大小为 10,起始页为 0。
2. 返回值类型:使用 Page<User>,直接返回分页结果,包含总记录数、总页数等元信息。
Service 层
示例代码
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public Page<User> getUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
}
关键点
1. 方法参数:接收 Pageable 对象,用于封装分页参数。
2. 返回结果:直接返回 Page<User>,由 Repository 提供。
Repository 层
Spring Data JPA 已内置分页支持,因此无需额外改动,只需确保 Repository 继承自 JpaRepository。
示例代码
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
前端示例请求
假设分页参数为 page 和 size,请求示例如下:
GET /api/users?page=1&size=5
返回结果示例
Spring Data JPA 自动封装分页结果,返回类似以下的 JSON:
{
"content": [
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob", "email": "bob@example.com" }
],
"pageable": {
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"offset": 5,
"pageNumber": 1,
"pageSize": 5,
"paged": true,
"unpaged": false
},
"last": false,
"totalPages": 10,
"totalElements": 50,
"size": 5,
"number": 1,
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"numberOfElements": 5,
"first": false,
"empty": false
}
优化分页结果(可选)
如果不想返回冗余的分页元数据,可以自定义响应格式:
修改 Controller
@GetMapping
public Map<String, Object> getUsers(
@PageableDefault(size = 10, page = 0) Pageable pageable) {
Page<User> page = userService.getUsers(pageable);
Map<String, Object> response = new HashMap<>();
response.put("users", page.getContent());
response.put("currentPage", page.getNumber());
response.put("totalItems", page.getTotalElements());
response.put("totalPages", page.getTotalPages());
return response;
}
自定义返回的 JSON
{
"users": [
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob", "email": "bob@example.com" }
],
"currentPage": 1,
"totalItems": 50,
"totalPages": 10
}
总结
• Controller 层:接受分页参数 Pageable。
• Service 层:直接调用 Repository 的分页方法。
• Repository 层:无需额外修改,Spring Data JPA 自动支持分页。
• 可选优化:根据业务需求,定制化分页响应格式。
通过改造,分页参数不会存储到实体中,同时接口可以支持灵活的分页查询。
在 Spring Boot 中,默认情况下,分页参数的名称是 page 和 size,如果需要自定义这些参数的名称,可以使用 @PageableDefault 和 @Qualifier 注解进行设置。
自定义分页参数名称的实现方法
通过 @RequestParam 手动绑定自定义参数名,然后构造一个 Pageable 对象传递给 Service 层。
修改 Controller
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public Page<User> getUsers(
@RequestParam(name = "currentPage", defaultValue = "0") int page,
@RequestParam(name = "pageSize", defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
return userService.getUsers(pageable);
}
}
示例请求
GET /api/users?currentPage=1&pageSize=5
自定义分页参数的全局配置(更高级方法)
如果希望在全局范围内统一自定义分页参数名称,可以自定义 HandlerMethodArgumentResolver 来修改默认的分页参数行为。
自定义配置类
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class CustomWebMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
resolver.setPageParameterName("currentPage"); // 自定义分页参数名称
resolver.setSizeParameterName("pageSize"); // 自定义每页大小参数名称
resolver.setOneIndexedParameters(true); // 支持从 1 开始的分页
resolvers.add(resolver);
}
}
Controller 使用分页参数(无需手动绑定)
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public Page<User> getUsers(Pageable pageable) {
return userService.getUsers(pageable);
}
}
示例请求
GET /api/users?currentPage=1&pageSize=5
返回结果示例
无论使用哪种方法,Spring Data JPA 都会处理分页逻辑,返回包含分页元信息和数据内容的结果,例如:
{
"content": [
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob", "email": "bob@example.com" }
],
"pageable": {
"sort": {
"empty": true,
"sorted": false,
"unsorted": true
},
"offset": 5,
"pageNumber": 1,
"pageSize": 5,
"paged": true,
"unpaged": false
},
"totalPages": 10,
"totalElements": 50,
"last": false,
"size": 5,
"number": 1,
"numberOfElements": 5,
"first": false,
"empty": false
}
总结
1. 自定义分页参数名称:
• 方法 1:在 Controller 中使用 @RequestParam 手动绑定。
• 方法 2:全局配置,通过自定义 PageableHandlerMethodArgumentResolver 修改默认行为。
2. 关键点:
• setPageParameterName(String):设置分页参数名称。
• setSizeParameterName(String):设置每页大小的参数名称。
• setOneIndexedParameters(boolean):设置是否支持 1 基索引。
3. 推荐方式:
• 若需要全局统一修改参数名称,优先选择方法 2,代码更简洁。