Hibernate Criteria com subquery
É realmente impressionante como o Hibernate pode fazer consultas sql usando Criteria. Ontem precisei fazer uma consulta e cheguei à conclusão que seria necessário o uso de subquery.
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:
1 2 3 4 5 6 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Entity @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:
1 2 3 4 5 6 7 8 9 10 11 12 | @Entity @SequenceGenerator(name = "sq_vitamin", sequenceName = "sq_vitamin") public class Vitamin { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "sq_vitamin") private Integer id; private String name; // getters, setters... } |
Em uma busca simples, poderíamos recuperar uma lista de todas os frutos que contém a vitamina C:
1 2 3 4 5 6 7 8 9 10 | Criteria criteria = session.createCriteria(Fruit.class, "fruit"); criteria.createAlias("fruit.vitamins", "vitamin"); criteria.add(Restrictions.eq("vitamin.name", "Vitamin C"), Criteria.LEFT_JOIN); List<fruit> fruits = criteria.list(); for (Fruit f: fruits) { System.out.println("Fruit: " + f.getName()); System.out.println("--Vitamin: " + f.getName()); } |
Como pedimos, esta consulta retorna uma lista de frutos. Porém, cada fruto retornado conterá apenas uma vitamina: a vitamina C!
1 2 3 4 5 6 | 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!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 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<fruit> list = criteria.list(); for (Fruit fruit : list) { System.out.println("Fruit: " + fruit.getName()); System.out.println("--Vitamins: " + fruit.getVitamins()); } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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=? ) |
1 2 3 4 5 6 | 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.

Um comentário
Muito obrigado, vc salvou meu dia hoje!!!!!
Deixe Seu Comentário