Executando verificação de segurança...
2

Criando uma camada de projeção mais limpa sobre a API de Criteria do JPA

Se você desenvolve sistemas utilizando Java com JPA, provavelmente já passou pela necessidade de realizar consultas retornando apenas alguns atributos de uma determinada entidade.

À primeira vista, isso pode parecer um problema simples. Porém, quando não tratado da forma correta, com o tempo o sistema tende a acumular consultas desnecessárias ou sobrecarregadas com atributos que não serão utilizados.

Em muitos cenários reais, o desenvolvedor precisa buscar apenas o “id” e o “nome” de uma entidade. No entanto, por conta do tamanho e da complexidade do sistema, acaba sendo difícil identificar se já existe uma consulta que retorne exatamente esse resultado. Em outros casos, acaba-se reutilizando métodos que carregam a entidade completa, apenas para extrair posteriormente os dados realmente necessários.

É nesse contexto que surge o ProjectionQuery. Seu objetivo é simplificar e organizar consultas baseadas em projeção, oferecendo uma forma mais clara e expressiva de selecionar apenas os dados que a aplicação realmente precisa.


Como o ProjectionQuery funciona na prática

Após adicionar a dependência ao seu projeto, você só precisa criar um class(ou um record) e definir quais campos devem ser projetados.

@Projection(of = Customer.class)
public record CustomerBasicData(
        @ProjectionField Long id,
        @ProjectionField String name,
        @ProjectionField("address.city.name") String city,
        @ProjectionField("address.city.state.name") String state
) { }

Esta classe atua como a representação final da consulta. Independentemente de quantos atributos existam na entidade Customer, apenas os campos id, name, city e state serão selecionados do banco de dados.

Note que city e state são atributos aninhados, recuperados por meio de relacionamentos definidos na entidade Customer.

O SQL final (simplificado) gerado para esta consulta será semelhante ao seguinte:

select
   id,
   name,
   city.name,
   state.name,
from customer
inner join address on customer.address = address.id
inner join city on address.city = city.id
inner join state on city.state = state.id

Agora que definimos nossa classe de projeção, podemos usar o ProjectionProcessor, que atua como mecanismo de execução para as consultas.

Existem duas maneiras principais de executar esta consulta:

Nos exemplos a seguir, assumimos um contexto JPA no qual o serviço ProjectionProcessor recebe uma instância de EntityManager, responsável por executar as consultas.

Execução simplificada

ProjectionProcessor processor = new ProjectionProcessor(entityManager);
List<CustomerBasicData> customers = processor.execute(CustomerBasicData.class);

Usando a classe ProjectionQuery
A classe ProjectionQuery ajuda a construir consultas mais avançadas, permitindo a adição de filtros, ordenação, paginação e outras configurações.

ProjectionProcessor processor = new ProjectionProcessor(entityManager);

ProjectionQuery<Customer, CustomerBasicData> query = ProjectionQuery
    .fromTo(Customer.class, CustomerBasicData.class)
    .filter("address.city.name", ProjectionFilterOperator.EQUAL, "São Paulo")
    .order("name", OrderDirection.ASC)
    .paging(0, 20)
    .distinct();

List<CustomerBasicData> customers = processor.execute(query);

ProjectionQuery pode ser usado tanto de forma independente quanto integrado em aplicações Spring Boot.

Para obter mais detalhes, exemplos adicionais e documentação completa, consulte a página do projeto no GitHub.

Carregando publicação patrocinada...