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.