springdatajpaSpecification复杂查询+分页查询当Repository接⼝继承了JpaSpecificationExecutor后,我们就可以使⽤如下接⼝进⾏分页查询:
/**
* Returns a {@link Page} of entities matching the given {@link Specification}.
*
* @param spec can be {@literal null}.
* @param pageable must not be {@literal null}.
* @return never {@literal null}.
*/
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
结合jpa-spec可以很容易构造出Specification:
public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.eq(StringUtils.Name()), "name", Name())
.Age()), "age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%", "%me")
.build();
return personRepository.findAll(specification, new PageRequest(0, 15));
}
单表查询确实很简单,但对复杂查询,就复杂上些了:
public List<Phone> findAll(SearchRequest request) {
Specification<Phone> specification = Specifications.<Phone>and()
.
eq(StringUtils.Brand()), "brand", "HuaWei")
.eq(StringUtils.PersonName()), "person.name", "Jack")
.build();
return phoneRepository.findAll(specification);
}
这⾥主表是phone,使⽤了person的name做条件,使⽤⽅法是person.name。
jpa-spec内部会分析person.name,如下代码:
public From getRoot(String property, Root<T> root) {
if (ains(".")) {
String joinProperty = StringUtils.split(property, ".")[0];
return root.join(joinProperty, JoinType.LEFT);
} else {
return root;
}
}
就可看到它⽤了root.join,那就有⼀个问题,如果有两个person字段的条件,那就要再join⼀次,就会⽣成这样的sql: select * from phone left outer join person on XX=XX left outer join person XX=XX.
这样肯定不满⾜需求。这应该也是jpa-spec的⼀个bug吧
为了解决这个问题,可以使⽤它提供的另⼀种⽅式查询:
public List<Phone> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.between("age", 10, 35)
.predicate(StringUtils.Name()), ((root, query, cb) -> {
Join address = root.join("addresses", JoinType.LEFT);
return cb.("street"), "Chengdu");
}))
.build();
return phoneRepository.findAll(specification);
}
这要就可以解决⼤多数情况了,除了分页
看下正常的单表分页+排序查询:
public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.eq(StringUtils.Name()), "name", Name())
.
gt("age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%")
.build();
Sort sort = Sorts.builder()
.desc(StringUtils.Name()), "name")
.asc("birthday")
.build();
return personRepository.findAll(specification, new PageRequest(0, 15, sort));
}
如果在此基础上增加关联,如下代码:
public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.predicate(StringUtils.Name()), ((root, query, cb) -> {
Join address = root.join("addresses", JoinType.LEFT);
return cb.("street"), "Chengdu");
}))
.eq(StringUtils.Name()), "name", Name())
.gt("age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%")
.build();
Sort sort = Sorts.builder()
.desc(StringUtils.Name()), "name")
.asc("birthday")
.build();
return personRepository.findAll(specification, new PageRequest(0, 15, sort));
}
就会发现addresses的延迟加载失效,⽣成很多查询addresses的语句,解决⽅案如下:
public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()sort of link什么意思
.predicate(StringUtils.Name()), ((root, query, cb) -> {
Join address;
if (Long.class != ResultType()) {
address = (Join) root.fetch("addresses", JoinType.LEFT);
} else {
address = root.join("addresses", JoinType.LEFT);
}
return cb.("street"), "Chengdu");
}))
.eq(StringUtils.Name()), "name", Name())
.gt("age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%")
.
build();
Sort sort = Sorts.builder()
.desc(StringUtils.Name()), "name")
.asc("birthday")
.build();
return personRepository.findAll(specification, new PageRequest(0, 15, sort));
}
⾄此,⽤Specification查询就应该够⽤了,再配合JpaRepository (SimpleJpaRepository)提供的⽅法和@Query注解⽅法,和criteria api 查询,这四种JPA查询就可以解决⼤多数应⽤问题了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。