Wednesday, August 26, 2015

Fazendo fetch via Criteria da JPA sem Metamodel Class

Outra dica relacionada a API de Criteria da JPA 2. A questão agora é: como realizar uma consulta flexível, com critérios variados, carregar os dados de uma entidade complementar (fetch) sem usar a classe gerada via Metamodel.

Como exemplo vou usar uma estrutura bem simples, duas entidades: Categoria e Mercadoria. O clássico relacionamento One-To-Many, sendo que uma Categoria possui várias Mercadoria(s). Nesse caso, a Categoria é a entidade raiz para construção da consulta.

O cenário é: gerar um relatório com as Categorias cuja as Mercadorias tenham preço entre R$ 500,00 e R$ 899,00. Além das informações das Categorias, o relatório também deve exibir as informações das Mercadorias que se enquadram nessa faixa de valores.

O pedaço de código a seguir é uma alternativa para implementar essa funcionalidade:
public final class CategoriaSpecification {
  
  public static Specification<Categoria> byPrecoComMercadoria(BigDecimal precoDe, 
      BigDecimal precoAte) {
    return new Specification<Categoria>() {
      @Override
      public Predicate toPredicate(Root<Categoria> root,
          CriteriaQuery<?> query, CriteriaBuilder builder) {
        FetchParent<Categoria, Mercadoria> fetch = root.fetch("mercadorias");
        Join<Categoria, Mercadoria> join = 
          (Join<Categoria, Mercadoria>) fetch; //truque

        return builder.between(join.get("preco"),
          precoDe, precoAte);
      }
    };
  }
  ...
}

O "truque" é usar o método fetch em root, mas converter a visão do objeto para Join. Os tipos Join e Fetch tem em comum a interface FetchParent. Com Join é possível definir predicados na construção da query. Note que nesse exemplo, eu uso o componente de Specification do Spring Data JPA.

www.yaw.com.br

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