JPA 2.0 CriteriaQuery on tables in @ManyToMany relationship

匿名 (未验证) 提交于 2019-12-03 09:02:45

问题:

I have two entities in a @ManyToMany relationship.

// Output has 4 other @ManyToOne relationships if that matters     @Entity @Table public class Output {     @Id public String address;     @ManyToMany(targetEntity = Interval.class,                 cascade = CascadeType.ALL,                 fetch = FetchType.LAZY)     @JoinTable(name = "output_has_interval",                joinColumns = {@JoinColumn(name = "output_address",                                            referencedColumnName = "address")},         inverseJoinColumns = {@JoinColumn(name = "interval_start",                                           referencedColumnName = "start"),                               @JoinColumn(name = "interval_end",                                            referencedColumnName = "end")})     Collection<Interval> intervals;  @IdClass(IntervalPK.class) // I'll omit this one. @Entity @Table public class Interval {     @Id public Calendar start;     @Id public Calendar start;     @ManyToMany(targetEntity = Output.class,                 mappedBy = "intervals",                 cascade = CascadeType.ALL,                 fetch = FetchType.LAZY)     public Collection<Output> outputs; 

The join table is called output_has_interval between output and interval.

How do I do CriteriaQuery like this?

SELECT `output`.`address` FROM   `output`, `output_has_interval`, `interval` WHERE  `output`.`address` = `output_has_interval`.`output_address` AND    `interval`.`start` = `output_has_interval`.`interval_start` AND    `interval`.`end` = `output_has_interval`.`interval_end` AND    `interval`.`start` >= '2011-04-30' 

This works as expected if I issue it in MySQL.

(I have the corresponding static meta model classes as well, on request I'll could post them - nothing fancy tho'.)

CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Output> cq = cb.createQuery(Output.class); Root<Output> root= cq.from(Output.class); CollectionJoin<Output, Interval> join = root.join(Output_.intervals); Expression<Calendar> start = join.get(Interval_.start); Predicate pred = cb.greaterThanOrEqualTo(start, /* calendar for '2011-04-30' */); cq.where(pred); TypedQuery<Output> tq = em.createQuery(cq); 

However tq.getResultList returns every output row from my database. Any idea?

(On a side note: Hibernate (the provider I'm using) generates many select statements when I issue this query, one for every relationship Output has, sometimes more.)

Edit.: I wrote:

tq.getResultList returns every output row from my database

To clarify it: it returns more than just every output row from my database. It actually does a join using output and interval however the predicate:

`interval`.`start` >= '2011-04-30' 

doesn't get satisfied.

回答1:

Ok, I'll managed to solve my riddle on my own.

First of all: the whole problem originated from the fact that I'm a lousy programmer. I iterated over TypedQuery<Output>.getResultList() and accessed every Interval in Output.intervals in a recursive manner, thus Hiberate loaded lazily the requested objects generating a handful of select statements.

However I had to get a hold of those Interval instaces somehow. The following change to my CriteriaQuery did the trick.

CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Tuple> cq = cb.createTupleQuery(); // or createQuery(Tuple.class) Root<Output> root= cq.from(Output.class); // from clause CollectionJoin<Output, Interval> join = root.join(Output_.intervals); Path<String> addressPath = root.get(Output_.address); // mind these Path objects Path<Calendar> startPath = join.get(Interval_.start); // these are the key to success! cq.multiselect(addressPath, startPath); // select clause Expression<Calendar> start = join.get(Interval_.start); Predicate pred = cb.greaterThanOrEqualTo(start, /* calendar for '2011-04-30' */); cq.where(pred); // where clause TypedQuery<Tuple> tq = em.createQuery(cq); // holds Tuples for (Tuple tuple : tq.getResultsList()) {     String address = tuple.get(addressPath);     Calendar start = tuple.get(startPath); ... 

Edit

I've just realized that I could've used Path<T> objects instead Expression<T> objects (or vice versa) as Path<T> extends Expression<T>. Oh well...



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!