Primeiro, fiz a consulta por um query console do meu banco para ter certeza. E um pouco descrente de que Criteria iria resolver o meu problema 100%, procurei por subquery na documetação do Hibernate.
Encontrei a solução e, para a minha surpresa, a consulta sql gerada foi exatamente a que eu havia feito no query console do meu banco de dados.
Então, vamos ao exemplo!
Suponhamos que temos registros de frutos e que, cada fruto contém uma lista de vitaminas:
Fruit: Pineapple
--Vitamins: [Vitamin C, Vitamin B6]
Fruit: Orange
--Vitamins: [Vitamin C, Vitamin B6]
Fruit: Banana
--Vitamins: [Vitamin C, Vitamin A]
Nossas classes, já mapeadas com annotations, ficariam assim:
Fruit.java:
- @SequenceGenerator(name = "sq_fruit", sequenceName = "sq_fruit")
- public class Fruit {
-
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO, generator = "sq_fruit")
- private Integer id;
- private String name;
- @ManyToMany(cascade = CascadeType.ALL)
- @ForeignKey(name = "fk_Fuit", inverseName = "fk_Vitamin")
- @JoinTable( name = "Fuits_Vitamins",
- joinColumns = @JoinColumn(name = "Fruit_id"),
- inverseJoinColumns = @JoinColumn(name = "Vitamin_id"))
- private List<Vitamin> vitamins;
-
- // getters, setters...
-
- }
Vitamin.java:
Em uma busca simples, poderíamos recuperar uma lista de todas os frutos que contém a vitamina C:
- Criteria criteria = session.createCriteria(Fruit.class, "fruit");
- criteria.createAlias("fruit.vitamins", "vitamin");
- criteria.add(Restrictions.eq("vitamin.name", "Vitamin C"), Criteria.LEFT_JOIN);
-
- List
fruits = criteria.list(); -
- for (Fruit f: fruits) {
- }
Como pedimos, esta consulta retorna uma lista de frutos. Porém, cada fruto retornado conterá apenas uma vitamina: a vitamina C!
Fruit: Pinnapple
--Vitamin: Vitamin C
Fruit: Orange
--Vitamin: Vitamin C
Fruit: Banana
--Vitamin: Vitamin C
Foi exatamente o que pedimos quando adicionamos Restrictions.eq("vitamin.name", "Vitamin C") para a criteria.
Até aqui tudo bem, se apenas fossem necessárias os frutos. E se precisássemos agora fazer a mesma consulta (por "vitamica C"), mas gostariámos de ver todas as vitaminas que cada fruto contém? Usamos subquery!
- // todos frutos que contém a vitamina C
- DetachedCriteria dc = DetachedCriteria.forClass(Fruit.class, "fruit");
- dc.createAlias("fruit.vitamins", "vitamin", Criteria.INNER_JOIN);
- dc.add(Restrictions.eq("vitamin.name", "Vitamin C"));
- dc.setProjection(Property.forName("fruit.id"));
-
- // mas quero ver as outras vitaminas que o fruto tem
- Criteria criteria = session.createCriteria(Fruit.class, "fruit");
- criteria.createAlias("fruit.vitamins", "vitamin", Criteria.LEFT_JOIN);
- criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
- criteria.add(Property.forName("fruit.id").in(dc));
-
- List
list = criteria.list(); -
- for (Fruit fruit : list) {
- }
O método in() da classe Property recebe uma DetachedCriteria. Uma DetachedCriteria nos permite criar uma consulta fora de uma sessão, para ser executada posteriormente em uma sessão arbitrária.
A saída no console, com o logger da query:
- Hibernate:
- select
- this_.id as id1_1_,
- this_.name as name1_1_,
- vitamins3_.Fruit_id as Fruit1_3_,
- vitamin1_.id as Vitamin2_3_,
- vitamin1_.id as id0_0_,
- vitamin1_.name as name0_0_
- from
- Fruit this_
- left outer join
- Fuits_Vitamins vitamins3_
- on this_.id=vitamins3_.Fruit_id
- left outer join
- Vitamin vitamin1_
- on vitamins3_.Vitamin_id=vitamin1_.id
- where
- this_.id in (
- select
- fruit_.id as y0_
- from
- Fruit fruit_
- inner join
- Fuits_Vitamins vitamins3_
- on fruit_.id=vitamins3_.Fruit_id
- inner join
- Vitamin vitamin1_
- on vitamins3_.Vitamin_id=vitamin1_.id
- where
- vitamin1_.name=?
- )
Fruit: Pineapple
--Vitamins: [Vitamin C, Vitamin B6]
Fruit: Orange
--Vitamins: [Vitamin C, Vitamin B6]
Fruit: Banana
--Vitamins: [Vitamin C, Vitamin A]
Tem outro exemplo aqui, usando Detached queries and subqueries.
Por Rodrigo Facholi em http://blog.dclick.com.br/2008/10/09/hibernate-criteria-com-subquery/pt/
Nenhum comentário:
Postar um comentário
Comentários sobre assuntos não relacionados ao objetivo deste blog serão removidos.