Criteria Queries en Spring Data


Aunque Spring Data nos ofrece fáciles maneras de obtener resultados de base de datos, en muchas ocasiones necesitamos un acercamiento programatico; por eso en este artículo vamos a ver como construir Criteria Queries en Spring Data a través de Spring Boot.

Hay en ocasiones, que necesitamos crear una query basada en condiciones o por múltiples campos, para ese tipo de casos hacer la query con Criteria será lo más ideal.

Dependencias Spring Data

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

Con este starter de Spring Boot añadiremos todas las dependencias de JPA que se requieran.

Crear Criteria Queries extendiendo Repository

En muchas ocasiones, tenemos que crear queries en las que las queries automáticas de Spring Data, no son suficientes, como añadir múltiples condiciones, para esos casos, podemos usar el API Criteria.

Para que sigamos pudiendo usar JpaRepository y así tener los métodos por defecto, vamos a crear una interfaz que extienda de JpaRepository y de la interfaz en la que definimos métodos propios.

EntityManager será necesario para usar API Criteria.
interface ResourceRepository<T> {
    List<T> findByNameAndSurname(String name, String title);
}

interface UserRepository extends JpaRepository<User, Long>, ResourceRepository<User> {
}

@Repository
class UserRepositoryImpl implements UserRepository {
 
  @PersistenceContext
  EntityManager em;

@Override
public List<User> findUsersByNameAndSurname(String name, String surname) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<User> cq = cb.createQuery(User.class);
 
    Root<User> user = cq.from(User.class);
    List<Predicate> predicates = new ArrayList<>();
     
    if (name != null) {
        predicates.add(cb.equal(user.get("name"), name));
    }
    if (surname != null) {
        predicates.add(cb.equal(user.get("surname"),surname));
    }
    cq.where(predicates.toArray(new Predicate[0]));
 
    return em.createQuery(cq).getResultList();
}

Al declarar ResourceRepository como dependencia, Spring buscará la implementación y la usará para llamar a los métodos que hemos definido dentro de la interfaz. De esta manera tendremos nuestro nuevo método y a los métodos que nos proporciona JPA.

Queries con JPA specification

Spring Data contiene la interfaz org.springframework.data.jpa.domain.Specification, con unos cuantos métodos para poder encapsular un predicado.

La mejor manera de ver como funcionan las specification, es con un ejemplo:

Vamos a crear una clase en la que se devuelva Specification:

public class specificationExample {

  public static Specification<UserDbo> getName(String name) {
     return (Specification<UserDbo>) (user, query, cb) -> {
       cb.equal(user.get("name"), name);
   }
  }
}

Con el ejemplo anterior sería el equivalente a hacer una query de Sql en el que el where sería user.name = ‘name’, aunque la potencia de usar specification, es poder añadir condiciones como en el siguiente ejemplo:

public class specificationExample {

  public static Specification<UserDbo> getName(String name, String surname) {
     return (Specification<UserDbo>) (user, query, cb) -> {
       List<Predicate> predicates = new ArrayList<>();            
       predicates.add(cb.equal(user.get("name"), name);
       
       if (null != surname) {
            predicates.add(cb.equal(user.get("surname"), surname);
       }
    return cb.and(predicates.toArray(Predicate[]::new));

    }
  }  
}

En el ejemplo anterior hemos añadido dos predicados a una lista de predicados en el que hemos establecido dos condiciones, una de ellas controlada de forma programática, siempre y cuando el apellido sea distinto de null.

Para usar estos métodos, podemos crear un bean de la clase specificationExample, y llamar al método; o extender del repositorio  org.springframework.data.jpa.repository.JpaSpecificationExecutor<T>. Sin olvidarnos extender de JpaRepository, para poder usar los métodos por defecto que nos da Spring Data.

Conclusión

En este artículo hemos visto como usar el API de Criteria, a través de extender Repository añadiendo nuevos métodos para que se realicen con Predicate y a través de Specification.



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *