I'm using JPA2/hibernate with this data model:
class Stock {
@ManyToOne
private StockGroup stockGroup;
private boolean visible;
}
class StockGroup {
@OneToMany(mappedBy = "stockGroup")
private List<Stock> stocks;
}
I would like to retrieve StockGroup's containing Stock's where visible==true
.
I've come up with this faulty code:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<StockGroup> q = cb.createQuery(StockGroup.class);
Root<StockGroup> r = q.from(StockGroup.class);
Join<StockGroup, Stock> j = r.join(StockGroup_.stocks, JoinType.INNER);
Predicate p = cb.equal(j.get(Stock_.visible), true);
// This becomes a cartesian product :(
List<StockGroup> l = em.createQuery(q.where(p)).getResultList();
// Stocks are not filtered on visible :(
l.get(0).getStocks();
Is it possible to retrieve the StockGroup and Stock Objects with one CriteriaQuery or can JPA only fill one type at once? Or can i add some Criteria when .getStocks()
is filled lazily?
The trick to doing this is returning a tuple containing an old-fashioned join between the Stock and the StockGroup, like this:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = cb.createQuery(Tuple.class);
Root<Stock> sRoot = q.from(Stock.class);
Root<StockGroup> sgRoot = q.from(StockGroup.class);
q.select(cb.tuple(sRoot, sgRoot)).where(
cb.and(cb.equal(sRoot.get(Stock_.stockGroup), sgRoot),
cb.isTrue(sRoot.get(Stock_.visible))));
List<Tuple> l = em.createQuery(q).getResultList();
The tuple is then not fully type safe, but you can reach it by position (or by alias, if you've given your select expressions or roots an alias):
for (Tuple t : l) {
Stock s = (Stock) t.get(0);
StockGroup sg = (StockGroup) t.get(1);
System.out.println("Stock is : " + s + " .... StockGroup: " + sg);
}
There's a good article on IBM DeveloperWorks on JPA2 Typesafe Queries.
Good luck in your JPA2 endeavors!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With