自己实现一个JPA中的分页

来源:互联网 发布:win7公用网络点不了 编辑:程序博客网 时间:2024/06/10 00:22

前言

最近有一个需求必须要使用原生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实体

原创粉丝点击