Ressources

Rappel

  • Nous avons déjà aborder la notion de spécification avec JPA, voir API Criteria
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}
  • L’interface JpaSpecificationExecutor permet de bénéficier de la méthode List<T> findAll(Specification<T> spec);

Interface Specification

L’interface Specification ne dispose que d’une seule méthode

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder);
}

Et cette interface vient nous aider à créer facilement des prédicats. En effet, avec API Criteria les prédicats ne sont pas faciles à externaliser et à réutiliser parce qu’il faut d’abord mettre en place le CriteriaBuilder, le CriteriaQuery et le Root.

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
Root<Customer> root = query.from(Customer.class);
 
Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
Predicate isLongTermCustomer = builder.lessThan(root.get("createdAt"), today.minusYears(2); 
 
query.where(builder.and(hasBirthday, isLongTermCustomer));
 
em.createQuery(query.select(root)).getResultList();

Créer des prédicats

L’interface Specification va donc nous aider à construire rapidement des prédicats

public CustomerSpecifications {
 
  public static Specification<Customer> customerHasBirthday() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.equal(root.get("birthday"), today);
      }
    };
  }
 
  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.lessThan(root.get("createdAt"), new LocalDate.minusYears(2));
      }
    };
  }
}

Utiliser des prédicats

Nous pouvons maintenant utiliser nos deux prédicats, car pour rappel CustomerRepository étend JpaSpecificationExecutor

customerRepository.findAll(hasBirthday());
customerRepository.findAll(isLongTermCustomer());
public class CustomerService {
    public List<Customer> giveReductionToLoyalCustomer() {
        return customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));
    }
}