JPA 分页查询(附代码)

提示:前提是你的 orm 层使用的是 jpa,mybatis 的兄弟可以跳过,jdk 是17.


前言

文章更多的是对我之前一篇文章的补充,springboot 集成 jpa 文章链接,这篇文章大致说了如何集成以及如何快速的 CRUD,但是关于分页漏掉了,这里做补充。


全文阅读约需要5分钟

一、如何分页?

这里我先贴代码,方便急需使用的同学。

实体(数据库需要创建对应的 table):

import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "user")
public class User {

  @Id
  @Column(length = 32)
  private String id;

  private String name;

  private Integer age;

  private Double money;

}

建表sql(mysql):

CREATE TABLE `user` (
  `id` varchar(32) NOT NULL COMMENT '主键ID',
  `name` varchar(50) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `money` decimal(10,2) DEFAULT NULL COMMENT '金额',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

controller 层:

import com.demo.domain.PageR;
import com.demo.domain.R;
import com.demo.entity.User;
import com.demo.repository.UserRepository;
import jakarta.annotation.Resource;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/test")
public class TestController {
    @Resource
    private UserRepository userRepository;

    @GetMapping("/page")
    public PageR<List<User>> findByPage(@RequestParam("pageNumber") Integer pageNumber,
                                             @RequestParam("pageSize") Integer pageSize) {
        // 因为底层的分页数是从0开始,所以需要将前端传递的参数减1,如果需要排序以及顺序还是倒序,可以参考我注释的部分,注释的部分是根据 name 字段倒叙。
        //Pageable pageable = PageRequest.of(pageNumber - 1, pageSize, Sort.by(Sort.Direction.DESC, "name"));
        Pageable pageable = PageRequest.of(pageNumber - 1, pageSize, Sort.unsorted());
        return R.pageOf(userRepository.findAll(pageable));
    }
}

持久层:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;

@NoRepositoryBean
public interface AbstractRepository<T, K> extends JpaRepository<T, K>, JpaSpecificationExecutor<T> {
}
import com.repository.AbstractRepository;
import com.demo.entity.User;
import jakarta.persistence.criteria.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

@Repository
public interface UserRepository extends AbstractRepository<User, String> {

    default Page<User> findAll(Pageable pageable) {
        Specification<User> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        return this.findAll(spec, pageable);
    }
}

到这里代码部分就已经完成了分页和排序,至于我 controller 层注释说的分页开始页,也可以让前端做个减 1 的操作,这样后端就不用处理;排序字段以及顺序还是倒序前端都可以传,后端做个默认处理都行,我一般都是用 createTime 做倒序排序,这个看需求。

二、如何携带过滤条件去分页?

这里我列举几个简单的场景供大家抄作业,后期关于其他条件过滤我会慢慢补充:

import com.repository.AbstractRepository;
import com.demo.entity.User;
import jakarta.persistence.criteria.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;

@Repository
public interface UserRepository extends AbstractRepository<User, String> {

    default Page<User> findAll(Integer age, String name, Date startDate, Date endDate, Pageable pageable) {
        Specification<User> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 精确匹配
            if (age != null) {
                predicates.add(criteriaBuilder.equal(root.get("age"), age));
            }
            // 模糊匹配
            if (name != null && !name.isEmpty()) {
                predicates.add(criteriaBuilder.like(root.get("name"), "%" + name + "%"));
            }
            // 范围匹配
            if (startDate != null && endDate != null) {
                predicates.add(criteriaBuilder.between(root.get("date"), startDate, endDate));
            } else {
                if (startDate != null) {
                    predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("date"), startDate));
                }
                if (endDate != null) {
                    predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("date"), endDate));
                }
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        return this.findAll(spec, pageable);
    }
}

这里大家不难发现我的参数都做了空判断,因为参数不可控,参数的判空是非常有必要的,这里的 date 字段是 user 实体中没有的,大家测试前需要自己加上去哈…


总结

基本以上代码能满足大家日常的开发了,至于如果大家遇到了第二部分没有的过滤,大家可以留言,我都会看的

文章随手而作,其中错误跟漏洞难免,望海涵,如果大家觉的还行,不要吝啬点赞收藏,谢谢啦 ~~~~~~~~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值