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
- BooleanBuilder Condicional: Condições são adicionadas ao predicado apenas se o usuário tiver fornecido o filtro correspondente na requisição.
- Projections.constructor: Mapeia os dados diretamente para o construtor da classe
UserSummaryDTOsem instanciar entidades gerenciadas pelo Hibernate. - 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
-
Slides da Aula
-
Quiz de Fixação
-
Exercícios Práticos
-
Desafio de Projeto