Spring Integración con QueryDSL

Seguimos con nuestra serie de cursos sobre acceso a datos con Spring, seguimos trabajando sobre el proyecto del tutorial anterior Spring Data JPA, en esta ocasión integraremos la librería QueryDSL a nuestro proyecto, la misma nos permite generar consultas de una manera, rápida, consistente y segura, esta librería fue en principio diseñada para HQL Hibernate pero hoy en día soporta varias tecnologías de persistencia de datos, como: JPA, JDBC, Lucene, MongoDB, etc. 

Utilizando QueryDSL nos olvidamos de escribir consultas en cadenas de texto, por lo que no requerimos los lenguajes, SQL, HQL, JPQL para consultar datos en sus respectivas tecnologías.

Para usar esta tecnología con Spring Data JPA requerimos las siguientes dependencias:

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>${querydsl.version}</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>${querydsl.version}</version>
</dependency>

También debemos configurar el siguiente maven plugin:

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>apt-maven-plugin</artifactId>
    <version>1.1.3</version>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>target/generated-sources</outputDirectory>
                <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

El procesador de anotaciones JPAAnnotationProcessor buscara las clases anotadas con @Entity y generará las clases especiales que serán utilizadas para crear las consultas, las mismas se almacenarán en el directorio target/generated-sources y normalmente tienen el mismo nombre de entidad más la letra inicial Q, por ejemplo, para la clase que hemos venido trabajando desde el inicio de esta seria de tutoriales, Product se creará una clase llamada QProduct.

En Netbeans damos clic derecho y seleccionamos Clean and Build una vez terminado el proceso tendremos lo siguiente:

spring integrando querydsl

La clase Product se define de la siguiente manera:

@Data
@Entity
public class Product {

    @Id private Long id;

    private String name;
    private Double price;

}

Si abrimos la clase QProduct, veremos lo siguiente:

@Generated("com.mysema.query.codegen.EntitySerializer")
public class QProduct extends EntityPathBase<Product> {

    private static final long serialVersionUID = -2093252856L;

    public static final QProduct product = new QProduct("product");

    public final NumberPath<Long> id = createNumber("id", Long.class);

    public final StringPath name = createString("name");

    public final NumberPath<Double> price = createNumber("price", Double.class);

    public QProduct(String variable) {
        super(Product.class, forVariable(variable));
    }

    public QProduct(Path<? extends Product> path) {
        super(path.getType(), path.getMetadata());
    }

    public QProduct(PathMetadata<?> metadata) {
        super(Product.class, metadata);
    }

}

De momento nos es necesario entender a profundidad esta clase, lo que debemos saber es que la misma nos permitirá realizar consultas sobre la entidad a la que representa, en este caso, la entidad Product.

Para utilizar la biblioteca QueryDSL en un proyecto Spring Data JPA debemos modificar el repositorio, para nosotros la interface ProductRepository que hemos creado previamente, la modificación es muy simple, solo debemos extender la interface QueryDslPredicateExecutor.

import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;

public interface ProductRepository extends
        CrudRepository<Product, Long>,
        QueryDslPredicateExecutor<Product> {

}

Esta interface se define como sigue:

QueryDSL en Spring Data JPA

Disponemos de los métodos findOne, findAll, count, para enviar consultas a las bases de datos, el primero nos devolverá un resultado, el segundo una colección y el tercero un conteo, la consulta en si la pasamos con el parámetro Predicate.

Para crear el Predicate primero debemos obtener un objeto QProduct esto lo hacemos a través del miembro estático, de la siguiente manera: QProduct qp = QProduct.product;.

ProductRepository pr = ctx.getBean(ProductRepository.class);

QProduct qp = QProduct.product;
Predicate prdct = qp.price.gt(100.0);    

pr.findAll(prdct).forEach(System.out::println);

querydsl mayor que

El Predicate prdct permite obtener una lista de todos los productos con un precio mayor a 100.0 dólares, este es creado mediante, qp.price.gt(100.0), tendremos disponible una variable Path por cada una de las propiedades de la entidad producto, ellas son: id, price, name, al acceder a cada una de ellas tendremos un conjunto de métodos que podemos usar para crear distintos tipos de consultas, en este primer caso usamos gt(100.0), gt indica mayor que (en ingles), de este modo encontraremos más de estos métodos.

Predicate helado = qp.name.contains("Ice");

querydsl consulta por texto

Este ejemplo busca todos los productos que en su nombre contengan la palabra "Ice", vemos que para consultar con respecto al nombre usamos qp.name seguido del método de consulta, contains("Ice"), otros métodos similares a este son: startsWith("...") y endsWith("...") que usaríamos para obtener los productos cuyos nombre empiecen o terminen con el texto indicado.

Para crear consultas más complejas podemos utilizar expresiones booleanas para combinar varias consultas, por ejemplo, usando el método and para combinar las dos consultas creadas previamente obtendríamos una lista de todos los productos con un precio mayor a 100.0 y que además en su nombre este la palabra “Ice”.

ProductRepository pr = ctx.getBean(ProductRepository.class);

QProduct qp = QProduct.product;

BooleanExpression precio = qp.price.gt(100.0);    
BooleanExpression helado = qp.name.contains("Ice");

Predicate helados_caros = helado.and(precio);

pr.findAll(helados_caros).forEach(System.out::println);

querydsl expresiones booleanas

En el siguiente código de ejemplo veremos como listar todos los productos no nulos y ordenarlos según el precio de manera ascendente.

pr.findAll(qp.isNotNull(), qp.price.asc())
        .forEach(System.out::println);

Este otro ejemplo nos muestra como contar los productos que tienen un precio entre los valores indicados.

long count = pr.count(qp.price.between(50.0, 220.0));
System.out.println("cantidad: " + count);

De momento quedamos aquí, debes saber que QueryDSL cuenta con gran cantidad de métodos para crear consultas, necesitaríamos una serie de tutoriales para verlas todas, pero una vez comprendas el funcionamiento, utilizarlas será sencillo, nos vemos en la próxima.

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Python Binance API