QUERY DINAMICHE CON SPRING DATA E SPECIFICATION

Quante volte vi sarà capitato di sviluppare una API Rest nell'ecosistema Spring Boot e di dover esporre dati da un database, sia esso relazionale o meno, in modo pulito restituendo solo un subset delle occorrenze e filtrando per specifiche proprietà della risorsa interrogata?

Ecco che le Specification rappresentano uno strumento utile in tutti questi casi in cui abbiamo la necessità di applicare dei filtri in modo condizionale su una determinata entità o su altre ad essa collegate, senza ricorrere a codici verbosi con metodi DAO dai nomi impronunciabili.

Partiamo con l'esempio. Lo starter è la risorsa sulla quale costruire una ricerca filtrata:

@Entity
public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String firstname; private String lastname; private String address; private String email; // standard getters and setters }

Fatto questo, procediamo con la creazione del repository. A differenza di quanto fatto usualmente, in questo caso è necessario implementare anche l'interfaccia JpaSpecificationExecutor che appunto consente l'esecuzione delle specification con le JPA Criteria API.

@Repository
public interface UserRepository extends JpaRepository<User>, JpaSpecificationExecutor<User> {
}

L'ultimo passo è la creazione della classe con la logica per l'applicazione dei filtri: si tratta in sostanza di costruire una serie di predicati in modo condizionale a seconda dei filtri valorizzati nell'oggetto di request inviato dal client.


package com.grimaldi.cargo.repositories.specification;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.data.jpa.domain.Specification;

import com.grimaldi.cargo.dto.statistiche.request.RequestFaccineDTO;
import com.grimaldi.cargo.model.Account;
import com.grimaldi.cargo.model.reports.DettaglioReportFaccine;
import com.grimaldi.cargo.model.reports.ReportFaccine;

public class UserSpecification implements Specification<User> {

	private static final long serialVersionUID = -1418612780146044594L;

	private final UserRequestDTO criteria;

	public UserSpecification(UserRequestDTO criteria) {
		this.criteria = criteria;
	}

	@Override
	public Predicate toPredicate(Root<User> root, CriteriaQuery arg1, CriteriaBuilder criteriaBuilder) {
		
		List<Predicate> predicates = new ArrayList<>();

		if(StringUtils.hasText(criteria.getFirstname())) {
			predicates.add(criteriaBuilder.equal(root.get("firstname"), criteria.getFirstname()));
		}
		if(StringUtils.hasText(criteria.getLastname())) {
			predicates.add(criteriaBuilder.equal(root.get("year"), criteria.getLastname()));
		}
		if(StringUtils.hasText(criteria.getEmail())) {
			predicates.add(criteriaBuilder.equal(root.get("year"), criteria.getEmail()));
		}
        
           return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
  
      }

}

Nell'esempio sopra riportato, la classe UserSpecification presenta un attributo denominato criteria, che "wrappa" le informazioni sulle quali filtrare gli utenti a db.

Creata la logica di "filtraggio", sarà possibile passare questo predicato in input al repository della risorsa User:

var spec = new UserSpecification(requestDTO);
var limit = requestDTO.getLimit() != null ? requestDTO.getLimit() : LIMIT; 
var page = requestDTO.getPage() != null ? requestDTO.getPage() - 1 : 0;
		
Page<User> records = userRepository.findAll(spec, PageRequest.of(page, limit, Sort.by("id").descending()));
	

Ecco che il gioco è fatto...il risultato è un service snello, non verboso e pulito. L'intera logica di filtraggio è demandata alla Specification evitando di ricorrere alle query in stile Spring Data con nomi complessi e col tempo poco leggibili.

Se ti è piaciuto l'articolo condividilo con qualche tuo conoscente in modo. Se invece ritieni che io abbia scritto qualche "scemenza", non esitare a commentare il post con suggerimenti o richieste.

Il codice di questo articolo è disponibile sul mio GitHub all'indirizzo:

Git source code



Comments

Popular posts from this blog

AGGIORNAMENTO INVESTIMENTI 2° TRIMESTRE 2024

AGGIORNAMENTO INVESTIMENTI 1° TRIMESTRE 2024

HO COMPRATO UNO XIOAMI 14 ULTRA