Specifications dynamic query
Sometimes when we query an entity, the given condition is not fixed, so we need to dynamically construct the corresponding query statement. In Spring Data JPA, we can query through Jpa Specification Executor interface. Compared with JPQL, its advantage is type-safe and more object-oriented.
Jpa Specification Executor Interface
Method defined in the JpaSpecification Executor interface:
import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.lang.Nullable; public interface JpaSpecificationExecutor<T> { //Query an object on condition Optional<T> findOne(@Nullable Specification<T> var1); //Query Object Sets Based on Conditions List<T> findAll(@Nullable Specification<T> var1); //Query and paginate according to conditions //pageable: Paging parameters //Return value: Paging pageBean, provided by spring data JPA Page<T> findAll(@Nullable Specification<T> var1, Pageable var2); //Sort Query Query // Sort: Sort parameter List<T> findAll(@Nullable Specification<T> var1, Sort var2); //Statistical query long count(@Nullable Specification<T> var1); } //Remarks // If the NULL value can be passed in, it is marked as @Nullable, and if not, it is marked as @Nonnull.
For Jpa Specification Executor, we can find that this interface method is basically defined around the Specification interface. We can simply understand that a Specification constructs a query condition.
——> We need to customize our own Specification implementation class.
Specfication interface
In the Specification interface, only one method is defined as follows:
import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; //Root: The root object of the query (any attribute of the query can be retrieved from the root object) //CriteriaQuery: Top-level query object, custom query mode (Understanding: Normally not used) //Criteria Builder: A query constructor used to construct query conditions that encapsulate many query condition methods public interface Specification<T> { Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3); }
Methodological correspondence
Method | SQL Correspondence Syntax |
---|---|
equle | filed = value |
gt(greaterThan ) | filed > value |
lt(lessThan) | filed < value |
ge(greaterThanOrEqualTo ) | filed >= value |
le( lessThanOrEqualTo) | filed <= value |
notEqule | filed != value |
like | filed like value |
notLike | filed not like value |
and | Conditions 1 and 2 and 3... And condition n |
or | Conditions 1 or 2 or 3... Or condition n |
Example
/ ** Customize dynamic queries * (1) Instance Specification Interface *—— Rewriting the toPredicate method ** root: Gets the object properties that need to be queried ** Criteria Builder: Constructs query conditions and encapsulates many query conditions internally (fuzzy query, precise matching) * (2) Invoking JpaSpecification Executor Interface Method Query * (3) Use of query results */
Example 1, equal exact query
@Test public void testSpec01(){ //(1) Implementing Specification Interface Specification<Student> spec = new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //1. Get the attributes of the comparison Path<Object> name = root.get("stuName"); //2. Constructing Query Conditions Predicate predicate = criteriaBuilder.equal(name, "Xie"); return predicate; } }; //(2) Invoking JpaSpecification Executor Interface Method Query Optional<Student> optional = repository.findOne(spec); // (3) Use of query results if (optional.isPresent()){ System.out.println("Query out the results:"+optional.get()); }else { System.out.println("No query results!!!"); } }
Example 2, like fuzzy query
/** * like Fuzzy query */ @Test public void testSpec2(){ Specification<Student> spec =new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Path<Object> name = root.get("stuName"); Predicate predicate = criteriaBuilder.like(name.as(String.class), "%Zhao%"); return predicate; } }; List<Student> students = repository.findAll(spec); students.forEach(a->System.out.println(a.toString())); }
Example 3, Combining and Queries
/** * Composite query */ @Test public void testSpec3(){ Specification<Student> spec =new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Path<Object> name = root.get("stuName"); Path<Object> age = root.get("age"); Predicate predicate01 = criteriaBuilder.ge(age.as(Integer.class), 20); Predicate predicate02 = criteriaBuilder.equal(name,"Xie"); Predicate predicate = criteriaBuilder.and(predicate01, predicate02); return predicate; } }; List<Student> students = repository.findAll(spec); students.forEach(a->System.out.println(a.toString())); }
Example 4, combined or queries
/** * Combined or query */ @Test public void testSpec4(){ Specification<Student> spec =new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Path<Object> name = root.get("stuName"); Path<Object> age = root.get("age"); Predicate predicate01 = criteriaBuilder.ge(age.as(Integer.class), 20); Predicate predicate02 = criteriaBuilder.equal(name,"Xie"); Predicate predicate = criteriaBuilder.or(predicate01, predicate02); return predicate; } }; List<Student> students = repository.findAll(spec); students.forEach(a->System.out.println(a.toString())); }
Example 5, Query Sorting
/** * Query ranking */ @Test public void testSpec5(){ Sort sort =new Sort(Sort.Direction.ASC,"age"); Specification<Student> spec =new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Path<Object> name = root.get("stuName"); Path<Object> age = root.get("age"); Predicate predicate01 = criteriaBuilder.ge(age.as(Integer.class), 20); Predicate predicate02 = criteriaBuilder.equal(name,"Xie"); Predicate predicate = criteriaBuilder.or(predicate01, predicate02); return predicate; } }; List<Student> students = repository.findAll(spec,sort); students.forEach(a->System.out.println(a.toString())); }
Example 6, Query Paging
/** * Query paging */ @Test public void testSpec6(){ Sort sort =new Sort(Sort.Direction.ASC,"age"); PageRequest pageRequest = PageRequest.of(1,2,sort); Specification<Student> spec =new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Path<Object> name = root.get("stuName"); Path<Object> age = root.get("age"); Predicate predicate01 = criteriaBuilder.ge(age.as(Integer.class), 20); Predicate predicate02 = criteriaBuilder.equal(name,"Xie"); Predicate predicate = criteriaBuilder.or(predicate01, predicate02); return predicate; } }; Page<Student> studentPage = repository.findAll(spec, pageRequest); long totalElements = studentPage.getTotalElements();//Get the total number System.out.println("Quantity of data for query:"+totalElements); int totalPages = studentPage.getTotalPages();//Get the total number of pages System.out.println("Number of data pages for query:"+totalElements); List<Student> students = studentPage.getContent();//Get all the content students.forEach(a->System.out.println(a.toString())); }