Pular para conteúdo

Aula 19 - Consultas Avançadas com Spring Data JPA e QueryDSL 🔎

Objetivo Pedagógico

Objetivo: Construção de consultas dinâmicas complexas, projeções de alta performance (DTO Projections), paginação eficiente e filtros dinâmicos com QueryDSL.


📑 1. Fundamentos Teóricos & Análise Técnica

O desenvolvimento de APIs corporativas frequentemente exige recursos de filtragem avançada com múltiplos parâmetros opcionais (pesquisar clientes por nome, faixa de data de cadastro, status financeiro e cidade simultaneamente). A abordagem tradicional baseada em interpolação de strings SQL ou múltiplos métodos derivados em repositórios resulta em explosão de complexidade e risco de segurança.

A combinação de Spring Data JPA com QueryDSL oferece a solução de engenharia definitiva: 1. Consultas Fluentes com Type Safety: O QueryDSL gera classes de metadados tipados (prefixadas com Q, ex: QUser.user) a partir das entidades JPA durante a compilação. 2. Filtros Dinâmicos com BooleanBuilder: Permite montar predicados lógicos condicionais (builder.and(...)) sem concatenar strings de consulta manualmente. 3. Projeções de Interface e DTO: Em vez de carregar a entidade completa na memória (com todas as suas relações pesadas), a consulta seleciona apenas os campos estritamente necessários (SELECT new com.dto.UserSummary(...)), reduzindo o consumo de memória na JVM e o tráfego de rede do banco de dados.

📐 Arquitetura Conceitual & Diagrama de Fluxo

graph TD
    Client["Filtro HTTP: ?status=ACTIVE&minAmount=500"] --> Controller["RestController"]
    Controller --> Predicate["BooleanBuilder (QueryDSL)"]
    Predicate --> Query["JPAQueryFactory / Repository"]
    Query --> SQL["SQL Gerado Dinâmico e Parametrizado"]
    SQL --> DTO["DTO Projection (Sem carregar entidades pesadas!)"]
    style Client fill:#e1f5fe,stroke:#01579b
    style Predicate fill:#fff3e0,stroke:#e65100
    style DTO fill:#e8f5e9,stroke:#2e7d32

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Tipagem Segura em Consultas: Erros de digitação em nomes de colunas geram erros de compilação, nunca exceções em produção. - Projeções de DTO Eficientes: Eliminação do problema de carregar grafos inteiros de entidades para montar telas simples. - Paginação Nativa com Pageable: Suporte a consultas paginadas transparentes com Page<T> e contagem de registros otimizada. - Prevenção de N+1 Queries: Uso estratégico de joinFetch em consultas complexas.


🛠️ 2. Implementação Prática em Spring Data JPA, Hibernate e QueryDSL

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// UserRepositoryCustomImpl.java (Filtro Dinâmico com QueryDSL)
package com.empresa.core.repository;

import com.empresa.core.domain.QUser;
import com.empresa.core.dto.UserFilterDTO;
import com.empresa.core.dto.UserSummaryDTO;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class UserRepositoryCustomImpl {

    private final JPAQueryFactory queryFactory;

    public UserRepositoryCustomImpl(JPAQueryFactory queryFactory) {
        this.queryFactory = queryFactory;
    }

    public List<UserSummaryDTO> findUsersByCriteria(UserFilterDTO filter) {
        QUser user = QUser.user;
        BooleanBuilder builder = new BooleanBuilder();

        if (filter.name() != null && !filter.name().isBlank()) {
            builder.and(user.name.containsIgnoreCase(filter.name()));
        }
        if (filter.active() != null) {
            builder.and(user.active.eq(filter.active()));
        }

        return queryFactory
            .select(Projections.constructor(UserSummaryDTO.class, user.id, user.name, user.email))
            .from(user)
            .where(builder)
            .limit(50)
            .fetch();
    }
}

💡 Análise Passo a Passo do Código

  1. BooleanBuilder Condicional: Condições são adicionadas ao predicado apenas se o usuário tiver fornecido o filtro correspondente na requisição.
  2. Projections.constructor: Mapeia os dados diretamente para o construtor da classe UserSummaryDTO sem instanciar entidades gerenciadas pelo Hibernate.
  3. Proteção contra Injeção: O QueryDSL parametriza todas as variáveis internamente através da API PreparedStatement do JDBC.

🎯 3. Próximos Passos & Sequência Didática