When the Repository interfaces inherited JpaSpecificationExecutor, we can use the following interface paging query:
/** * 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);
Binding jpa-spec can be easily constructed Specification:
jpa-spec github Address: https://github.com/wenhao/jpa-spec
public Page<Person> findAll(SearchRequest request) { Specification<Person> specification = Specifications.<Person>and() .eq(StringUtils.isNotBlank(request.getName()), "name", request.getName()) .gt(Objects.nonNull(request.getAge()), "age", 18) .between("birthday", new Date(), new Date()) .like("nickName", "%og%", "%me") .build(); return personRepository.findAll(specification, new PageRequest(0, 15)); }
Single-table queries is really very simple, but complex inquiries, some of the complex:
public List<Phone> findAll(SearchRequest request) { Specification<Phone> specification = Specifications.<Phone>and() .eq(StringUtils.isNotBlank(request.getBrand()), "brand", "HuaWei") .eq(StringUtils.isNotBlank(request.getPersonName()), "person.name", "Jack") .build(); return phoneRepository.findAll(specification); }
Phone here is the main table, using a person's name to make the conditions, methods of use are person.name.
Internal jpa-spec analyzes person.name, the following code:
public From getRoot(String property, Root<T> root) { if (property.contains(".")) { String joinProperty = StringUtils.split(property, ".")[0]; return root.join(joinProperty, JoinType.LEFT); } else { return root; } }
You can see it with the root.join, then there is a problem, if there are two person field conditions, it would once again join, it will generate this sql:
select * from phone left outer join person on XX=XX left outer join person XX=XX.
This certainly does not meet the demand. It should also be a bug jpa-spec bar
To solve this problem, you can use another way it provides query:
public List<Phone> findAll(SearchRequest request) { Specification<Person> specification = Specifications.<Person>and() .between("age", 10, 35) .predicate(StringUtils.isNotBlank(jack.getName()), ((root, query, cb) -> { Join address = root.join("addresses", JoinType.LEFT); return cb.equal(address.get("street"), "Chengdu"); })) .build(); return phoneRepository.findAll(specification); }
To this can be resolved in most cases, in addition to paging
Look normal single-table query paging + Sort:
public Page<Person> findAll(SearchRequest request) { Specification<Person> specification = Specifications.<Person>and() .eq(StringUtils.isNotBlank(request.getName()), "name", request.getName()) .gt("age", 18) .between("birthday", new Date(), new Date()) .like("nickName", "%og%") .build(); Sort sort = Sorts.builder() .desc(StringUtils.isNotBlank(request.getName()), "name" .Asc ()"birthday") .build(); return personRepository.findAll(specification, new PageRequest(0, 15, sort)); }
If the increase on the basis of this association, the following code:
public Page<Person> findAll(SearchRequest request) { Specification<Person> specification = Specifications.<Person>and() .predicate(StringUtils.isNotBlank(jack.getName()), ((root, query, cb) -> { Join address = root.join("addresses", JoinType.LEFT); return cb.equal(address.get("street"), "Chengdu"); })) .eq(StringUtils.isNotBlank(request.getName()), "name", request.getName()) .gt("age", 18) .between("birthday", new Date(), new Date()) .like("nickName", "%og%") .build(); Sort sort = Sorts.builder() .desc(StringUtils.isNotBlank(request.getName()), "name") .asc("birthday") .build(); return personRepository.findAll(specification, new PageRequest(0, 15, sort)); }
You will find addresses of lazy loading failure to generate a lot of queries addresses statement, the solution is as follows:
public Page<Person> findAll(SearchRequest request) { Specification<Person> specification = Specifications.<Person>and() .predicate(StringUtils.isNotBlank(jack.getName()), ((root, query, cb) -> { Join address; if (Long.class != query.getResultType()) { address = (Join) root.fetch("addresses", JoinType.LEFT); } else { address = root.join("addresses", JoinType.LEFT); } return cb.equal(address.get("street"), "Chengdu"); })) .eq(StringUtils.isNotBlank(request.getName()), "name", request.getName()) .gt("age", 18) .between("birthday", new Date(), new Date()) .like("nickName", "%og%") .build(); Sort sort = Sorts.builder() .desc(StringUtils.isNotBlank(request.getName()), "name") .asc("birthday") .build(); return personRepository.findAll(specification, new PageRequest(0, 15, sort)); }
At this point, with Specification inquiries should be enough, coupled with the method JpaRepository (SimpleJpaRepository) provided annotations and @Query methods and criteria api query, the four JPA query can solve the problem for most applications.