Thursday, August 20, 2015

Usando Criteria da JPA 2 com Spring Data JPA

A flexibilidade do Spring Data JPA para desenvolver componentes de persistência e acesso a dados não é novidade. Nesse post vou descrever como utilizar o Criteria da JPA 2.0 em conjunto com o Spring Data JPA para gerar consultas avançadas. Essa abordagem é uma outra alternativa comparada ao QueryDSL.

O contrato Specification determina que um predicado (condição SQL) é criado a partir dos elementos Root, CriteriaQuery e CriteriaBuilder. Esses três elementos fazem parte da API de Criteria do JPA 2, mas são controlados e injetados pelo Spring.

O trecho a seguir, demonstra uma Specification para aplicar o filtro da categoria na consulta da entidade Mercadoria:

public final class MercadoriaSpecification {

  public static Specification<Mercadoria> byNome(String name) {
    return new Specification<Mercadoria>() {
      @Override
      public Predicate toPredicate(Root<Mercadoria> root,
          CriteriaQuery<?> query, CriteriaBuilder builder) {
        return builder.like(root.<String>get("nome"), 
            String.format("%s%", name.trim()));
      }
    };
  }
  ...
}

Essa Specification será encaminhada para o repositório, no momento da consulta. Veremos isso depois de analisar o repositório.

A interface Repository, sofre uma pequena modificação. Ela deve estender a interface JpaSpecificationExecutor, veja:

@Repository
public interface MercadoriaRepository
    extends JpaRepository<Mercadoria, Long>, JpaSpecificationExecutor<Mercadoria> {
}

Através dessa extensão é possível utilizar Specification nas consultas da entidade. O próximo trecho é usar o repositório p/ fazer a consulta de Mercadoria(s) de acordo com o predicado definido na Specification:

@RestController
@RequestMapping(value="/")
public class MercadoriaController {

  @Autowired
  private MercadoriaRepository repository;

  @RequestMapping(method = RequestMethod.GET, value="/{nome}")
  public List<Mercadoria> listByNome(@PathVariable String nome) {
    Specification<Mercadoria> specification = MercadoriaSpecification.byNome(nome);
    return Lists.newArrayList(repository.findAll(specification));
  }
  ...
}

Além da interface Specification, o Spring Data JPA também oferece a classe Specifications para facilitar a composição de múltiplos predicados. Subi um gist no meu GitHub com os trechos de código desse post e um outro exemplo de consulta com vários predicados simulando um formulário de filtros.

A referência desse post vem do blog do Spring.io.

www.yaw.com.br

4 comments:

Alexandre Camilo said...

Meu amigo, li a documentação do spring sobre o assunto, a especificação do jpa e não consegui entender como implementar essa interface, juntamente com criteria. Em poucas linhas vocÊ tirou totalmente minhas dúvidas. Parabéns!

Hinotori said...

Valeu man... você é o cara

Marlon Falzetta said...

Olá @edermag,

Uma ajuda, se puder.

Estou usando Spring Boot com Spring Data.

Tenho uma estrutura de classes onde eu preciso recuperar dados baseados em diversas entidades. Ex. Todos os alunos da turma Y da série X que estudam no turno da manhã.

Para resolver esse problema, primeiro criei consultas onde eu escrevia todo o SQL (@Query) e dizia para executar de forma nativa (nativeQuery = true).
Depois esbarrei num problema quando fiz a consultar ser paginada (@Pageable), porque não aceita queryes nativas. Para resolver adaptei a query para JPQL.
Agora me dei conta que posso estar fazendo algo simples, de forma complexa. Tem um jeito mais fácil? Seria usando Criteria? Com o Critéria eu consigo fazer filtros navegando pelas classes associadas?

Grande abraço.

Hinotori said...
This comment has been removed by the author.