如何基于Spring Data自定义分页实现非映射对象BananaDTO列表分页
这个问题我之前也帮人处理过,核心就是在自定义实现里手动处理分页参数和总记录数,再用Spring Data的分页类封装结果就行,具体步骤如下:
实现步骤
1. 更新Repository接口
首先在BananaRepository接口里定义支持分页的方法,参数用Pageable,返回类型指定为Page<BananaDTO>:
import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.Repository; public interface BananaRepository extends Repository<Banana, Long> { // 替换原有方法,新增带分页参数的版本 Page<BananaDTO> findAllBananes(Pageable pageable); }
这里接口泛型填
Banana是因为Spring Data需要关联对应的实体类,哪怕最终返回的是DTO,也不影响功能。
2. 编写自定义分页逻辑(BananaRepositoryImpl中)
在实现类里需要完成两个关键操作:查询分页后的DTO列表和统计符合条件的总记录数,最后用PageImpl封装结果返回。
首先定义对应的JPQL语句(注意你的BananaDTO必须有匹配参数的构造函数,比如BananaDTO(Long id, String name, String color)这类):
import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import java.util.List; public class BananaRepositoryImpl implements BananaRepository{ @Autowired EntityManager em; // 定义查询DTO的JPQL(替换成你实际的DTO构造逻辑) private static final String JPQL_FIND_BANANAS_DTO = "SELECT new com.yourpackage.dto.BananaDTO(b.id, b.name, b.color) FROM Banana b"; // 定义统计总条数的JPQL(必须和上面的查询条件完全一致,避免计数不准) private static final String JPQL_COUNT_BANANAS = "SELECT COUNT(b) FROM Banana b"; @Override public Page<BananaDTO> findAllBananes(Pageable pageable) { // 1. 查询分页后的DTO列表 TypedQuery<BananaDTO> query = em.createQuery(JPQL_FIND_BANANAS_DTO, BananaDTO.class); // 设置分页参数:起始位置和每页条数 query.setFirstResult((int) pageable.getOffset()); query.setMaxResults(pageable.getPageSize()); List<BananaDTO> bananaList = query.getResultList(); // 2. 查询总记录数 TypedQuery<Long> countQuery = em.createQuery(JPQL_COUNT_BANANAS, Long.class); long totalCount = countQuery.getSingleResult(); // 3. 构造Page对象返回 return new PageImpl<>(bananaList, pageable, totalCount); } }
3. 关键注意事项
- DTO构造函数匹配:JPQL里的
new BananaDTO(...)必须和DTO类的构造函数参数数量、类型完全对应,否则会抛出异常。 - 条件查询的一致性:如果查询带有过滤条件(比如WHERE子句),统计总条数的JPQL必须带上完全相同的条件,并且同步设置参数,示例如下:
// 带过滤条件的JPQL示例 private static final String JPQL_FIND_BANANAS_DTO = "SELECT new com.yourpackage.dto.BananaDTO(b.id, b.name, b.color) FROM Banana b WHERE b.color = :color"; private static final String JPQL_COUNT_BANANAS = "SELECT COUNT(b) FROM Banana b WHERE b.color = :color"; // 在方法中同步设置参数 query.setParameter("color", "yellow"); countQuery.setParameter("color", "yellow"); - 复杂查询的计数处理:如果JPQL涉及多表关联、分组操作,统计总条数时要避免重复计数,比如用
COUNT(DISTINCT b.id)代替COUNT(b)。
简化方案(逻辑简单时可用)
如果你的查询逻辑不复杂,完全可以不用写自定义Impl,直接在Repository接口上用@Query注解实现:
public interface BananaRepository extends Repository<Banana, Long> { @Query("SELECT new com.yourpackage.dto.BananaDTO(b.id, b.name, b.color) FROM Banana b") Page<BananaDTO> findAllBananes(Pageable pageable); }
这种情况下Spring Data会自动处理分页参数和总条数查询,省掉自定义Impl的代码,非常便捷。
内容的提问来源于stack exchange,提问作者user8451134




