02 maio 2016

Hibernate Criteria – Restrições em listas com subquery

Recentemente precisei criar um relatório específico para um projeto no qual há uma grande quantidade de filtros possíveis. Ao analisar esse cenário, eu e a equipe decidimos utilizar o Criteria do Hibernate por causa da facilidade de manipular dinamicamente os filtros. E, no meio da implementação, encontramos um problema no resultado da busca.

Vamos ao exemplo:

Numa lanchonete existem vários tipos de lanche. Cada lanche tem seus ingredientes. E, na minha busca, eu quero trazer só os lanches que possuem bacon.

Suponhamos que temos os registros a seguir:

Lanche: X-Salada
--Ingredientes: Pão, carne, queijo e salada.

Lanche: X-Egg Bacon
--Ingredientes: Pão, carne, queijo, ovo e bacon.

Lanche: X-Bacon 
--Ingredientes: Pão, carne, queijo e bacon.

Nossas classes ficariam assim:

@Entity
public class Lanche {
    @Id
    private Long id;

    private String nome;
    private List ingredientes;
}

@Entity
public class Ingrediente {

    @Id
    private Long id;

    private String nome;
}

Na nossa primeira tentativa de executar a busca, fizemos o seguinte código:

Criteria criteria = session.getCurrentSession().createCriteria(Lanche.class);
criteria.createAlias("ingredientes", "in", JoinType.LEFT_OUTER_JOIN);
criteria.add(Restrictions.eq("in.nome", "Bacon"));
criteria.list();

O resultado estava correto. Porém, cada lanche retornado continha somente bacon. E precisávamos de todos os ingredientes dos lanches. Retorno:

Lanche: X-Egg Bacon
--Ingredientes: Bacon

Lanche: X-Bacon
--Ingredientes: Bacon

Vimos, então, que não daria certo buscar desta forma. Para solucionar o problema, utilizamos subquery do Criteria. Refatoramos o código e ficou assim:

Criteria criteria = session.getCurrentSession().createCriteria(Lanche.class);

// Busca todos os lanches que possuem ingrediente bacon
DetachedCriteria lanches = DetachedCriteria.forClass(Lanche.class, "la");
lanches.createAlias("la.ingredientes", "in", JoinType.INNER_JOIN);
lanches.add(Restrictions.eq("in.nome", "Bacon"));
lanches.setProjection(Property.forName("la.id"));

// Lista os lanches com base nos ids retornados da busca acima.
criteria.add(Property.forName("id").in(lanches));

criteria.list();

E o resultado:

Lanche: X-Egg Bacon
--Ingredientes: Pão, carne, queijo, ovo e bacon.

Lanche: X-Bacon
--Ingredientes: Pão, carne, queijo e bacon.

Gabriel Suaki – Software Engineer at redspark.
GitHub: GSuaki
Twitter: @gsuaki

Gabriel Suaki
About Gabriel Suaki

Software Engineer at redspark Gabriel Suaki - Software Engineer at redspark.

Leave a Comment