您的位置:首页 > 编程语言 > ASP

结合SpringDataJPA中的PagingAndSortingRepository和 JpaSpecificationExecutor接口实现大数据量的数据分页查询

2017-09-23 10:54 2016 查看
其中PagingAndSortingRepository用于分页和排序功能的实现,JpaSpecificationExecutor用于条件查询(主要是针对一张表中的数据进行查询)

一、下面先针对这两个接口进行详细的介绍,第二部分为实际应用的实例,方便实践和测试:

第一部分:PagingAndSortingRepository

1.PagingAndSortingRepository接口提供了一下接口:

Iterable<T> findAll(Sort var1);   //得到经过排序的所有数据
Page<T> findAll(Pageable var1);   //经过分页之后的某一页的数据


其中第二个findAll(Pageable var1)中封装的功能特别强大,参数是Pageable类型,这个var1参数中包含了pageNumber和PageSize两个参数,当然,还可以添加Sort参数,在分页时进行排序。像这样:

Pageable page = PageRequest(pageNumber,pageSize,sort)
Pageable page = PageRequest(pageNumber,pageSize)


这个page对象中存储了所有分页的信息.

2.Pageable

Pageable 是一个接口,他的实现类是PageRequest。这个PageRequest有三个实现构造方法:

(下面的代码是从网上复制粘贴的,仅用于展示其操作)

//这个构造出来的分页对象不具备排序功能
public PageRequest(int page, int size) {
this(page, size, (Sort)null);
}
//Direction和properties用来做排序操作
public PageRequest(int page, int size, Direction direction, String... properties) {
this(page, size, new Sort(direction, properties));
}
//自定义一个排序的操作
public PageRequest(int page, int size, Sort sort) {
super(page, size);
this.sort = sort;
}


这里外加一个衍生查询的定义,需要的时候可以用到:

PagingAndSortingRepository接口中只有findAll方法,想要实现分页我就就要集成这个接口,当然我们在使用PagingAndSortingRepository提供的findAll方法的同时,还可以使用SpringDataJPA中Repository的衍生查询,(也就是自定义根据属性名称或者是添加@Query查询的方法,比如:findByAge) ,在这些衍生查询方法的参数中只要把pageable参数放置在最后一个,就可以实现同时分页的功能。像是这样(下面是一段手写的kotlin代码):

fun findByAge:Iterable<T>(pageNumber:int,pageSize:int,age:Int){
Val page = PageRequest(pageNumber,pageSize)
Val list = findByAge(age,page)
Return list
}


第二部分:

SpringDataJPA在提供分页的同时,也提供了类似于hibernate的Criteria的查询方式:

要使用这种方式,就需要继承我上面说过的接口:JpaSpecificationExecutor,这个接口有以下方法:

Optional<T> findOne(@Nullable Specification<T> var1);
List<T> findAll(@Nullable Specification<T> var1);
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
long count(@Nullable Specification<T> var1);


这个接口通过Specification来定义查询的条件,(也就是说,在这里,在使用Specification查询条件的同时,使用上面第三个方法,将Pageable作为参数传入,就能实现查询分页。)在实现Specification这个接口的同时,重写其中的toPredict方法,自定义查询的条件。用kotlin语言实现的方法如下:

class mySpec : Specification<User> {
override fun toPredicate(root: Root<User>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder): Predicate {
val userName: Path<String> = root["name"]
return criteriaBuilder.equal(userName, name)
}
}


上面是一个简单的equal的JPQL语句的构建,当然这里可以构建更多的查询方式,是这样来链接的,形成or或者and语句.

这里的 root[“name”]中的root是当前查询的表名称,name为root实体中的一个属性名。

CriteriaBuilder接口中提供了很多方法:

Equal:val predict:Predicate = criteriaBuilder.equal(userName, name)

Like: val predict:Predicate = criteriaBuilder.like(userName, “%${name}%”)

lessThan:val predict:Predicate = criteriaBuilder.lessThan(createDate, createAt)

等等。其中第一个参数为Path类型,第二个参数是查询条件,如果这个参数值符合查询条件,则会被筛选出来。

二、实践代码实例

这里关于这两个接口的详细介绍就完了,下面是我实际中写的代码实例:为了不暴露代码还要体现出使用的基本逻辑,暂时改为用User类来代替。

首先:Dao层的接口继承

interface PagingRepository : PagingAndSortingRepository<User, Int>, JpaSpecificationExecutor<User>


service层和controller层

然后,我这里是没有写serviceImpl的,因为这边代码量少,也不属于单独一个模块,所以直接将实现方法写在了controller层中。(哈哈,这里说代码量少…确实是少,因为我们初期如果不适用、SpringDataJPA中的接口,想要实现分页模糊查询,可能要写很多复杂的sql语句以及代码的实现,模糊查询写一些sql语句去实现,分页新建一个Page类,在每次请求使用这个工具类,并且可能还需要表与表之间的级联查询。好在我是在同一张表中查数据。 那么问题来了,怎么样实现大数据量中的符合条件的分页查询功能呢?这个就会很难链接到一起同时实现,我目前能想到的就是先吧所有的都查出来,再分页发送给前端,这样做只是减少了前端的鸭梨,后台还是同样的工作。 但是使用了这两个接口就不一样啦,短短几行代码就搞定。)

**—————————————————这里是分割线

下面实现 根据user 的name、age、createDate进行筛选查询分页功能的实现,其中,name进行模糊查询,age查询符合某个年龄段的User进行相等查询,createDate为符合某个用户创建时间段的查询。这三种要求进行and操作。

fun loadFilesByGroupTag(@RequestParam pageNumber: Int, @RequestParam pageSize: Int,@PathVariable userName: String, @RequestParam userAge: Int,@RequestParam createDate: Int): Page<Any> {

class mySpec : Specification<File> {
override fun toPredicate(root: Root<File>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder): Predicate {
val userName: Path<String> = root["name"]
val userAge: Path<Int> = root["Age"]
val userCreateDate: Path<Date> = root["createDate"]

//这里可以将上面的uName的定义直接写在下面的like语句中,但是这里equal语句是不能写的,会报错,除非单独写出来,通过Path<T>来定义他的类型。可以多加尝试。
val p1 = criteriaBuilder.like(userName,"%uName%") //uName为用户模糊筛选的值
val p2 = criteriaBuilder.equal(userAge,uAge)
val p3 = criteriaBuilder.lessThan(usercreateDate,"%createDate%") //小于...

return criteriaBuilder.and(p1,p2,p3) //如果是或语句,这里用or就ok
}
}

val pageable = PageRequest(pageNumber, pageSize)
val page = pagingRepository.findAll(mySpec(), pageable)
println("当前第几页:" + (page.number + 1))
println("当前页总共有几条数据:" + page.numberOfElements)
println("总共多少条符合条件的数据:" + page.totalElements)
println("总共多少页:" + page.totalPages)

return page
}


看明白了没?

不过针对上面这种多条件的查询,常涉及到的功能是多条件模糊查询的集合。而有些条件用户可能不想进行筛选,这时候,就涉及到上面的条件p1/p2/p3中可能有一个为空,或者两个为空。然后criteriaBuilder.and()或者criteriaBuilder.or()中的参数是不能为空的,这就遇到一个比较头疼的问题,用kotlin来解决的话,用机械式的笨拙方法解决,写出来的代码实在是太丑了,也就是吧这些条件添加到一个集合中,去空之后,再进行forEach到criteriaBuilder中。下面记录一下比较好的解决方法:

fun loadFilesByGroupTag(@RequestParam pageNumber: Int, @RequestParam pageSize: Int,@PathVariable userName: String, @RequestParam userAge: Int,@RequestParam createDate: Int): Page<Any> {

class mySpec : Specification<File> {
override fun toPredicate(root: Root<File>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder): Predicate {
val userName: Path<String> = root["name"]
val userAge: Path<Int> = root["Age"]
val userCreateDate: Path<Date> = root["createDate"]

val p1 = criteriaBuilder.like(userName,"%uName%")
val p2 = criteriaBuilder.equal(userAge,uAge)
val p3 = criteriaBuilder.lessThan(usercreateDate,"%createDate%")

val list = mutableListOf<Predicate>()
with(criteriaBuilder) {
uName?.let { list.add(like(userName, "%$uName%")) }
uAge?.let { list.add(like(userAge, "%$uAge")) }
createDate?.let { list.add(equal(userCreateDate, createDate)) }
}
return and(*list.toTypedArray()) //这里将list先装换为array,然后再展开,涉及到list的varags
}
}

val pageable = PageRequest(pageNumber, pageSize)
val page = pagingRepository.findAll(mySpec(), pageable)

return page
}


拓展:关于list的varags的操作

//Use spread operator, see example from M2 release notes472. Unfortunately, it is not described in main Koltin docs.
fun printAll(vararg a : String) {
for (item in a) println(item)
}

fun main(args: Array<String>) {
printAll("one", "two")
printAll(*args)
}


三、运算符集合

运算符:
/**
* 运算符
*/
public enum Operator {
/** 等于 */
eq(" = "),
/** 不等于 */
ne(" != "),
/** 大于 */
gt(" > "),
/** 小于 */
lt(" < "),
/** 大于等于 */
ge(" >= "),
/** 小于等于 */
le(" <= "),
/** 类似 */
like(" like "),
/** 包含 */
in(" in "),
/** 为Null */
isNull(" is NULL "),
/** 不为Null */
isNotNull(" is not NULL ");
Operator(String operator) {
this.operator = operator;
}
private String operator;
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
}


参考:kotlin中文官网

spring data jpa 利用JpaSpecificationExecutor做复杂查询

Spring Boot系列(五):spring data jpa的使用

SpringBoot第二讲利用Spring Data JPA实现数据库的访问(二)_分页和JpaSpecificationExecutor接口介绍
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  springdata
相关文章推荐