Proper use Spring Data JPA specification

Discussion about learning to share sharp distinctions of class, create Spring Data JPA is primarily intended to generate a query by name method to easily create queries through. Sometimes, however, we need to create complex queries, and can not use query builder. Code a lot of knowledge notes to others.

Spring Data JPA provides a programming model repository, the head of the model domain objects by each tube interface. These interfaces define two purposes: first, through the expansion JpaRepository, we get a bunch of generic CRUD methods, such as save, findAll, delete and so on. Secondly, this will allow the classpath Spring Data JPA repository infrastructure to scan the interface and create Spring Bean. A typical repository is shown as follows:

 1 public interface CustomerRepository extends JpaRepository<Customer, Long> {
 2 
 3   Customer findByEmailAddress(String emailAddress);
 4 
 5   List<Customer> findByLastname(String lastname, Sort sort);
 6 
 7   Page<Customer> findByFirstname(String firstname, Pageable pageable);
 8 
 9 }
10 
11  

 

To create complex queries, why should specify the specifications?

Yes, you can use to build complex queries Criteria API. To understand why you want to use standard, we consider a simple business needs. We will use the Criteria API and the subsequent specifications to achieve this requirement.

This is the use case: at the customer's birthday, we want to send coupons to all long-term customers. How do we retrieve a match?

We have two predicates:

 1 LocalDate today = new LocalDate();
 2 
 3 CriteriaBuilder builder = em.getCriteriaBuilder();
 4 
 5 CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
 6 
 7 Root<Customer> root = query.from(Customer.class);
 8 
 9 Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
10 
11 Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2);
12 
13 query.where(builder.and(hasBirthday, isLongTermCustomer));
14 
15 em.createQuery(query.select(root)).getResultList();
16 
17  

 

In the above code,

  • · The first line creates LocalDate to compare the customer's birthday and today's date.
  • · The following lines contain the necessary configuration settings for example JPA-based boilerplate code.
  • · Then, in the next two lines, we will build a predicate
  • · In the last two lines, one for connecting the two predicates, the last one to perform a query.

The main problem with this code is that the predicate is not easy to externalize and reuse, because you need to set CriteriaBuilder, CriteriaQuery and Root. In addition, since it is difficult to quickly deduce the intent of the code, so the poor readability of the code.

specification

To be able to define a reusable predicate, we have introduced a standardized interface, which is derived from the concept of Eric Evans' Domain Driven Design "book introduced. It is defined as the predicate specification entity, which is the meaning of the interface specification represents. In fact, it contains only one method:

1 public interface Specification<T> {
2 
3   Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
4 
5 }
6 
7  

 

Use Java 8, the code becomes very clear and easy to understand.

 1 public CustomerSpecifications {
 2 
 3   public static Specification<Customer> customerHasBirthday() {
 4 
 5 return (root, query, cb) ->{
 6 
 7 return cb.equal(root.get(Customer_.birthday), today);
 8 
 9 };
10 
11  }
12 
13  public static Specification<Customer> isLongTermCustomer() {
14 
15 return (root, query, cb) ->{
16 
17         return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
18 
19 };
20 
21 }
22 
23 }
24 
25  

 

Clients can now do the following:

1 customerRepository.findAll(hasBirthday());
2 
3 customerRepository.findAll(isLongTermCustomer());
4 
5  

 

Here, basically will prepare your CriteriaQuery, Root and CriteriaBuilder, apply the predicate by a given specification to create and execute queries.

We just created a reusable predicates that can be performed alone. We can use these individual predicates to meet our business needs combined. We have a helper class specification, it provides (and) and (or) method to connect atom specification.

1 customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));

 

 

Using only compared to the JPA Criteria API, it reads very fluently, improve readability and provide more flexibility. It should be noted here that the only proposed specification implementation requires quite a bit of coding.

Here are some of the advantages of specification:

  1. All the "basic" queries have been achieved, namely findById, save and delete
  2. Paging functionality out of the box. You can simply be a page from the object passed to the Service Controller to your repository, and can work (even ordering)!
  3. Use Spring's Specification API simpler than ordinary JPA, because you only need to create a predicate, without having to mess with EntityManager and PersistenceContext.

If you have anything to add or share, please leave a message in the comments section below.

I wish you happy learning!

Guess you like

Origin www.cnblogs.com/youruike1/p/12053692.html