Spring Boot 中各种分页查询方式完整示例

在 Spring Boot 中,实现分页查询有多种方式,下面我将介绍几种常见的分页实现方法,包括 Spring Data JPA 分页、MyBatis 分页、手动分页等。

1. Spring Data JPA 分页

这是最简单和常用的分页方式,Spring Data JPA 内置了分页支持。

1.1 基本实现

首先,添加依赖:

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

实体类:

java 复制代码
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
    // getters, setters, constructors...
}

Repository 接口:

java 复制代码
public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findByNameContaining(String name, Pageable pageable);
}

Service 层:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public Page<User> getUsersByName(String name, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
        return userRepository.findByNameContaining(name, pageable);
    }
}

Controller 层:

java 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;
    
    @GetMapping
    public ResponseEntity<Page<User>> getUsers(
            @RequestParam(required = false) String name,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        return ResponseEntity.ok(userService.getUsersByName(name, page, size));
    }
}

1.2 自定义返回结果

有时我们需要自定义返回数据结构:

java 复制代码
public class PageResult<T> {
    private List<T> content;
    private int page;
    private int size;
    private long totalElements;
    private int totalPages;
    // getters, setters...
    
    public static <T> PageResult<T> fromPage(Page<T> page) {
        PageResult<T> result = new PageResult<>();
        result.setContent(page.getContent());
        result.setPage(page.getNumber());
        result.setSize(page.getSize());
        result.setTotalElements(page.getTotalElements());
        result.setTotalPages(page.getTotalPages());
        return result;
    }
}

然后在 Controller 中使用:

java 复制代码
@GetMapping("/custom")
public ResponseEntity<PageResult<User>> getUsersCustom(
        @RequestParam(required = false) String name,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
    Page<User> userPage = userService.getUsersByName(name, page, size);
    return ResponseEntity.ok(PageResult.fromPage(userPage));
}

2. MyBatis 分页

2.1 使用 PageHelper

添加依赖:

XML 复制代码
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>最新版本</version>
</dependency>

Mapper 接口:

java 复制代码
@Mapper
public interface UserMapper {
    List<User> findByName(@Param("name") String name);
}

Service 层:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    public PageInfo<User> getUsersByName(String name, int page, int size) {
        PageHelper.startPage(page, size);
        List<User> users = userMapper.findByName(name);
        return new PageInfo<>(users);
    }
}

Controller 层:

java 复制代码
@GetMapping("/mybatis")
public ResponseEntity<PageInfo<User>> getUsersMybatis(
        @RequestParam(required = false) String name,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
    return ResponseEntity.ok(userService.getUsersByName(name, page, size));
}

2.2 MyBatis-Plus 分页

添加依赖:

XML 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>最新版本</version>
</dependency>

配置分页插件:

java 复制代码
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

Mapper 接口:

java 复制代码
public interface UserMapper extends BaseMapper<User> {
    // 继承基本方法
}

Service 层:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    public IPage<User> getUsersByName(String name, int page, int size) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.like("name", name);
        return userMapper.selectPage(new Page<>(page, size), wrapper);
    }
}

Controller 层:

java 复制代码
@GetMapping("/mybatis-plus")
public ResponseEntity<IPage<User>> getUsersMybatisPlus(
        @RequestParam(required = false) String name,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
    return ResponseEntity.ok(userService.getUsersByName(name, page, size));
}

3. 手动分页

当数据量不大或需要特殊处理时,可以使用手动分页:

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public List<User> getUsersManually(String name, int page, int size) {
        List<User> allUsers = userRepository.findByNameContaining(name);
        
        int fromIndex = page * size;
        if (fromIndex >= allUsers.size()) {
            return Collections.emptyList();
        }
        
        int toIndex = Math.min(fromIndex + size, allUsers.size());
        return allUsers.subList(fromIndex, toIndex);
    }
    
    public Map<String, Object> getUsersWithTotal(String name, int page, int size) {
        List<User> allUsers = userRepository.findByNameContaining(name);
        List<User> pageContent = getUsersManually(name, page, size);
        
        Map<String, Object> result = new HashMap<>();
        result.put("content", pageContent);
        result.put("currentPage", page);
        result.put("pageSize", size);
        result.put("totalElements", allUsers.size());
        result.put("totalPages", (int) Math.ceil((double) allUsers.size() / size));
        
        return result;
    }
}

Controller 层:

java 复制代码
@GetMapping("/manual")
public ResponseEntity<Map<String, Object>> getUsersManual(
        @RequestParam(required = false) String name,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
    return ResponseEntity.ok(userService.getUsersWithTotal(name, page, size));
}

4. WebMvc 分页参数自动绑定

可以创建一个分页参数类来自动绑定分页参数:

java 复制代码
public class PageParam {
    @Min(0)
    private int page = 0;
    
    @Min(1)
    @Max(100)
    private int size = 10;
    
    private String sort = "id,asc"; // 格式: field,direction
    
    // getters, setters...
    
    public Pageable toPageable() {
        String[] parts = sort.split(",");
        if (parts.length == 2) {
            return PageRequest.of(page, size, 
                Sort.by(Sort.Direction.fromString(parts[1]), parts[0]));
        }
        return PageRequest.of(page, size);
    }
}

Controller 使用:

java 复制代码
@GetMapping("/auto-page")
public ResponseEntity<Page<User>> getUsersAutoPage(
        @RequestParam(required = false) String name,
        PageParam pageParam) {
    return ResponseEntity.ok(userRepository.findByNameContaining(name, pageParam.toPageable()));
}

5. 响应式分页 (WebFlux)

如果使用 Spring WebFlux:

添加依赖:

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Repository:

java 复制代码
public interface ReactiveUserRepository extends ReactiveMongoRepository<User, String> {
    Flux<User> findByNameContaining(String name, Pageable pageable);
}

Service:

java 复制代码
@Service
public class ReactiveUserService {
    @Autowired
    private ReactiveUserRepository userRepository;
    
    public Mono<Page<User>> getUsers(String name, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        Flux<User> userFlux = userRepository.findByNameContaining(name, pageable);
        Mono<Long> countMono = userRepository.countByNameContaining(name);
        
        return Mono.zip(userFlux.collectList(), countMono)
            .map(tuple -> new PageImpl<>(tuple.getT1(), pageable, tuple.getT2()));
    }
}

Controller:

java 复制代码
@RestController
@RequestMapping("/api/reactive/users")
public class ReactiveUserController {
    @Autowired
    private ReactiveUserService userService;
    
    @GetMapping
    public Mono<ResponseEntity<Page<User>>> getUsers(
            @RequestParam(required = false) String name,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        return userService.getUsers(name, page, size)
            .map(ResponseEntity::ok)
            .defaultIfEmpty(ResponseEntity.notFound().build());
    }
}

总结

  1. Spring Data JPA 分页:最简单,适合大多数 CRUD 应用

  2. MyBatis 分页

    • PageHelper:简单易用

    • MyBatis-Plus:功能更强大

  3. 手动分页:灵活,适合特殊需求

  4. 自动绑定分页参数:提高代码整洁度

  5. 响应式分页:适合响应式应用

根据项目需求和技术栈选择合适的分页方式。对于大多数 Spring Boot 项目,Spring Data JPA 或 MyBatis-Plus 的分页功能已经足够强大且易于使用。

相关推荐
Flittly6 分钟前
【日常小问】Spring Cloud Gateway 5.x 跨域和路由配置踩坑实录
java·spring boot·spring cloud
斯特凡今天也很帅1 小时前
新建数据源报错No bean named ‘SqlSessionFactorykf‘ available
java·数据库·spring boot·mybatis
小钻风33661 小时前
Spring Boot WebSocket 两种集成方式深度解析
spring boot·后端·websocket
SuniaWang2 小时前
AgentX 专栏-00前言:一个Java开发者的Agent实践之路
java·人工智能·spring boot·langchain·系统架构
逍遥德2 小时前
Java编程高频的“踩坑点”-01:fastjson.JSON 转换时泛型擦除问题
java·spring boot·spring·系统架构·json
闪电悠米2 小时前
黑马点评短信登录01_session_sms_login
java·spring boot·redis·git·spring·面试
Advancer-2 小时前
黑马点评plus --异步秒杀重构升级
java·spring boot·重构·intellij-idea
happymaker06262 小时前
SpringBoot学习日记——DAY04(整合junit,myBatis)
spring boot·学习·junit
辰海Coding15 小时前
MiniSpring框架学习-完成的 IoC 容器
java·spring boot·学习·架构
Maiko Star20 小时前
* SpringBoot整合LangChain4j
java·spring boot·后端·langchain4j