我有两个@ManyToMany
关系的实体。
// 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;
联接表在output_has_interval
和output
之间被称为interval
。
我如何CriteriaQuery
这样做?
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'
如果我在MySQL中发布它,这可以正常工作。
(我也有相应的静态元模型类,根据要求我可以发布它们 - 没什么好看的。)
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);
但是tq.getResultList
会返回数据库中的每个output
行。有什么想法吗?
(旁注:Hibernate(我正在使用的提供程序)在发出此查询时生成许多select
语句,每个关系Output
都有一个,有时更多。)
编辑:我写道:
tq.getResultList
返回每一个 我的数据库中的output
行
澄清一下:它返回的不仅仅是数据库中的每个output
行。它实际上使用output
和interval
进行连接但是谓词:
`interval`.`start` >= '2011-04-30'
不满意。
答案 0 :(得分:2)
好的,我会自己解决问题。
首先:整个问题源于我是一个糟糕的程序员。我迭代TypedQuery<Output>.getResultList()
并以递归方式访问Interval
中的每个Output.intervals
,因此Hiberate懒惰地加载请求的对象,生成少量select
语句。
然而,我必须以某种方式掌握那些Interval
实例。我CriteriaQuery
的以下更改可以解决问题。
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);
...
我刚刚意识到我可以使用Path<T>
个对象代替Expression<T>
个对象(反之亦然)Path<T>
扩展Expression<T>
。哦,好吧......