最近有一个需求必须要使用原生SQL进行查询数据
这就带来了一个问题:没办法使用JPA直接传入Pageable对象进行分页查询
所以就需要自己实现一个类似JPA分页的功能
在查了源码之后也没有看到JPA到底是怎么实现的(如果有知道的请告诉我…)
所以自己封装了一个工具类来模拟了JPA的分页
项目github地址:https://github.com/wchstrife/JPA-MyPage
下面先让我们看一看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 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返回的每一个参数包含的内容
有了上面的参数,我们就需要模仿一个一样的分页
首先让我们先根据上面的参数写一个工具类:
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 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;
}
在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实体