背景
JPA对于简单类的查询还是不错的,但通常业务场景不会那么简单,必须用到sql语句来组成一些查询,这个时候我们最好就用手动分页了。类似于MyBatis的用法。
实体类
这是一个员工的实体,即将对他进行操刀
@Entity
@NamedQuery(name="Employee.findAll", query="SELECT e FROM Employee e")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private int id;
private Timestamp createtime;
private String empImage;
private String empName;
private String empNum;
private Timestamp lastupdatetime;
private String status;
......
}
Repository类
手写语句,这样的好处在于,全部自定义,功能强大,能想出什么强大的sql就能用到什么强大的功能。
(c.emp_num=?1 or ?1 is null)
这种是做个非空判断,如果是空,就跳过这个条件,不执行。
@Repository
public interface EmployeeRepositrory extends JpaRepository<Employee, Integer> {
@Query(value ="select * from employee c "
+ "where (c.emp_num=?1 or ?1 is null) and (c.status=?2 or ?2 is null) "
+ "order by c.lastupdatetime desc limit ?3,?4 ",nativeQuery = true)
List<Employee> findAllList(String empNum,String status,Integer pageNumber,Integer pageSize);
@Query(value ="select count(*) from employee c "
+ "where (c.emp_num=?1 or ?1 is null) and (c.status=?2 or ?2 is null) "
+ "order by c.lastupdatetime desc ",nativeQuery = true)
Integer countAllList(String empNum,String status);
}
Controller控制器
@PostMapping("/list")
public ApiReturnObject findAll(String empNum,String status, Integer pageNumber, Integer pageSize) {
//检查pageNumber, pageSize非空
CheckUtils.checkPageNumberPageSize(pageNumber, pageSize);
//获取数据
List<Employee> list=employeeRepositrory.findAllList(empNum, status,(pageNumber-1)*pageSize, pageSize);
//获取计数
Integer countNum=employeeRepositrory.countAllList(empNum, status);
//封装返回
return ApiReturnUtil.pageManual(pageNumber, pageSize, countNum, list);
}
(pageNumber-1)*pageSize, pageSize
这个是因为,mysql的limit x,y;x代表从第几条数据开始,y代表获取多少
所以从数学的角度来讲,这里是(pageNumber-1)*pageSize代表第几条数据开始。
例如一共12条纪录,pageNumber=2,pageSize=10
(pageNumber-1)*pageSize, pageSize=10,10
代表从第10条开始,连续拿10条(09,1019)
如果不会Limit的,这里补充一下,Limit是mysql的语法select * from table limit m,n 其中m是指记录开始的index,从0开始,表示第一条是指从第m+1条开始, 取n条。
ApiReturnUtil&ApiReturnObject
这些根据大家的需要封装,这里这是做个示例
public class ApiReturnObject implements Serializable{
String errorCode="00";
Object errorMessage;
Object returnObject;
String pageNumber;
String pageSize;
String totalElements;
String totalPages;
public ApiReturnObject(String pageNumber,String pageSize,String totalElements,String totalPages,String errorCode, Object errorMessage, Object returnObject) {
super();
this.pageNumber = pageNumber;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.returnObject = returnObject;
this.pageSize = pageSize;
this.totalElements = totalElements;
this.totalPages = totalPages;
}
}
public static ApiReturnObject pageManual(Integer pageNumber, Integer pageSize,Integer countNum, List returnObject) {
return new ApiReturnObject(pageNumber+"",pageSize+"",countNum+"",getTotalPages(countNum, pageSize),"00","success",returnObject);
}
public static String getTotalPages(Integer countNum, Integer pageSize) {
if((countNum%pageSize)==0) {
return ((countNum/pageSize))+"";
}else {
return ((countNum/pageSize)+1)+"";
}
}
返回格式如下
{
"errorCode": "00", //返回码,一般00代表成功
"errorMessage": "success", //返回信息
"pageNumber": "1", //分页页码
"pageSize": "10", //分页大小
"returnObject": [{......}], //返回的list,这里不陈列
"totalElements": "12",//count语句查询出来有多少条纪录
"totalPages": "2" //一共分多少页,通过总数/分页大小再+1可以算出((countNum/pageSize)+1)
}
总结
到这里就完成啦,怎么样,是不是很好用,定制性很强。这里有几点技巧提示一下:
- 例如你要多关联一些表,那就尽情的再sql语句里面left join吧,如果要先!
- 如果有字段是主表没有的,希望加上去的,就加个@Transient注释,代表不实体化这个字段,但是查询又可以关联出来
- 判断多用(c.emp_num=?1 or ?1 is null)这个形式的,可以少走很多弯路(除非有特殊需求)
- 模糊查询可以参考 SpringDataJPA之@Query with LIKE(JPA模糊查询) https://zhengkai.blog.csdn.net/article/details/80681502