自己实现一个JPA中的分页

前言

最近有一个需求必须要使用原生SQL进行查询数据
这就带来了一个问题:没办法使用JPA直接传入Pageable对象进行分页查询
所以就需要自己实现一个类似JPA分页的功能
在查了源码之后也没有看到JPA到底是怎么实现的(如果有知道的请告诉我…)
所以自己封装了一个工具类来模拟了JPA的分页

项目github地址:https://github.com/wchstrife/JPA-MyPage

JPA中的分页

下面先让我们看一看JPA中Page是如何使用的
我们先建一个学生Student的实体(省略了构造、get、set方法)
Stuent:

@Entity
@Table(name = "student")
public class Student implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid")
    @Column(name = "id", columnDefinition = "varchar(64) binary")
    private String id;

    @Column(name = "name", length = 128)
    private String name;

    @Column(name = "age")
    private int age;

    @Column(name = "idcard")
    private String idCard;

    @Column(name = "school_name")
    private String schoolName;

controller中定义好映射的路径

    @GetMapping("/get/jpaPage")
        public JSONObject getAllByPage(HttpServletRequest request, HttpServletResponse response, @RequestParam("page") int page, @RequestParam("size") int size){

        Page<Student> studentPage =  studentService.findAllStudentByPage(page, size);

        JSONObject jsonObject = (JSONObject) JSON.toJSON(studentPage);

        return jsonObject;
    }

访问该路径,可以看到使用JPA的分页给我们返回的数据

{
    "number": 0,
    "last": false,
    "numberOfElements": 2,
    "size": 2,
    "totalPages": 3,
    "sort": null,
    "content": [
        {
            "idCard": "41524134",
            "name": "王宸昊",
            "id": "1",
            "schoolName": "北京科技大学",
            "age": 21
        },
        {
            "idCard": "41524133",
            "name": "张三",
            "id": "2",
            "schoolName": "北京大学",
            "age": 23
        }
    ],
    "first": true,
    "totalElements": 5
}

可以看到Page返回的每一个参数包含的内容

  • number : 当前为第几页
  • last: 是否为最后一页
  • numberOfElements: 当前页有多少条数据
  • size : 每页有多少条数据
  • totalPages: 总共有多少页
  • content:数据的内容
  • first: 是否为第一页
  • totalElements : 总共有多少条数据

自己实现Page

有了上面的参数,我们就需要模仿一个一样的分页
首先让我们先根据上面的参数写一个工具类:

public class PageUtil {

    /**
     * 每页大小
     */
    private int size;

    /**
     * 当前页为第几页
     */
    private int number;

    /**
     * 是否为第一页
     */
    private boolean first = false;

    /**
     * 是否为最后一页
     */
    private boolean last = false;

    /**
     * 总共有多少页
     */
    private int totalPages;

    /**
     * 总共有多少条数据
     */
    private int totalElements;

    /**
     * 当前页一共有多少条数据
     */
    private int numberOfElements;

    /**
     * 数据
     */
    private List content = new ArrayList();

    /**
     * 根据传入的当前多少页
     * @param size
     * @param number
     * @param totalElements
     */
    public PageUtil(int size, int number, int totalElements) {

        this.size = size;

        this.totalElements = totalElements;

        this.number = number < 0 ? 0 : number;

        this.totalPages = totalElements % size == 0 ? totalElements/size : (totalElements/size) + 1;

        this.first = number == 0 ? true : false;

        this.last = number == this.totalPages ? true : false;

    }

注意,这里NumberOfElements和Content这两个属性需要在添加数据的时候set进去

然后我们在service中写我们的具体逻辑

/**
     * 自定义分页获取所有学生
     * @param page
     * @param size
     */
    public PageUtil findAllStudentByMypage(int page, int size){

        /*分页查询数据*/
        Query query = em.createNativeQuery("SELECT * FROM student s LIMIT " + page*size + "," + size);
        List<Student> studentList = query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();

        /*查询总共的数据条目*/
        query = em.createNativeQuery("SELECT COUNT(*) FROM student");
        Object object = query.getResultList().get(0);
        int totalElements = Integer.parseInt(object.toString());

        /*构建自定义Page对象*/
        PageUtil pageUtil = new PageUtil(size, page, totalElements);
        pageUtil.setNumberOfElements(studentList.size());
        pageUtil.setContent(studentList);

        return pageUtil;
    }
  1. 我们按照JPA的约定,传入的page是从0开始的,我们返回的时候第一页也是从0开始的。
  2. 第一条sql,使用sql中的limit关键字对数据进行分页查询,第一个参数是page*size
  3. 第二条sql是查询所有的数据共有多少条,然后对这个总共有多少条进行除法等运算,就可以得到一共有多少页数据(具体的逻辑在工具类的构造方法中

在controller中定义好路径

@GetMapping("/get/myPage")
    public JSONObject getAllByMyPage(HttpServletRequest request, HttpServletResponse response, @RequestParam("page") int page, @RequestParam("size") int size){
        PageUtil pageUtil = studentService.findAllStudentByMypage(page, size);

        JSONObject jsonObject = (JSONObject) JSON.toJSON(pageUtil);

        return jsonObject;
    }

访问该路径,得到数据:

{
    "number": 0,
    "last": false,
    "numberOfElements": 2,
    "size": 2,
    "totalPages": 3,
    "content": [
        {
            "id_card": "41524134",
            "name": "王宸昊",
            "school_name": "北京科技大学",
            "id": "1",
            "age": 21
        },
        {
            "id_card": "41524133",
            "name": "张三",
            "school_name": "北京大学",
            "id": "2",
            "age": 23
        }
    ],
    "first": true,
    "totalElements": 5
}

可以看到content中的内容的key的值跟JPA当中的有稍微的区别
这是因为我们从数据库查出的数据的属性是按照数据库中存储的key来进行转换的
所以这里的key是数据库中的字段
要解决这个小问题可以从query 的 resultList拿出每一条数据再转为Student实体

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值