【问题标题】:JPA 2.0 CriteriaQuery on tables in @ManyToMany relationship@ManyToMany 关系中表的 JPA 2.0 CriteriaQuery
【发布时间】:2011-08-03 20:46:12
【问题描述】:

我在@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;

连接表在outputinterval 之间称为output_has_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 行。它实际上使用outputinterval 进行连接,但是谓词:

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

不满意。

【问题讨论】:

    标签: java database jpa criteria jpa-2.0


    【解决方案1】:

    好的,我会设法自己解决我的谜题。

    首先:整个问题源于我是一个糟糕的程序员这一事实。我迭代了TypedQuery&lt;Output&gt;.getResultList() 并以递归方式访问了Output.intervals 中的每个Interval,因此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&lt;T&gt; 对象而不是 Expression&lt;T&gt; 对象(反之亦然),因为 Path&lt;T&gt; 扩展了 Expression&lt;T&gt;。哦,好吧……

    【讨论】:

    • 我正在使用 root.join 合成器,当我检查 Hibernate 日志时,我可以看到查询正在运行,但它计算的是我的对象的 id,而不是列出它们...
    • @Alex 好吧,没有看到你的代码,我不知道可能是什么问题。
    • 我创建了一个自定义类来实现规范。在这个类的方法 toPredicate() 中,我构建了一个谓词列表,并将这个数组转换为一个要返回的谓词,如下所示:return criteriaBuilder.and(filters.toArray(new Predicate[filters.size()]));。在我的谓词列表中,我添加了一个 JOIN,以从特定的 Group 获取 Employees(这是一个多对多关系)。 filters.add(criteriaBuilder.equal(root.join("groups").get("id"), this.groupId)); 在这个连接中,查询没有被调用,我只能在休眠日志中看到一个select count。感谢您的任何建议
    • @Alex 你能把你的代码发布到 pastebin 或其他地方吗?
    • @Alex 一些想法:在您的 toPredicate 方法中,您正在构建 where 表达式,仅据我所知。 select 子句中通常出现的部分在这里缺失。在我的示例中,您可以看到我调用了CriteriaQuery.from,它将告诉解析器/构建器实际上想要select * from Output 或类似的东西。您甚至没有在方法中使用 CriteriaQuery 对象。所以我猜错误是当你使用toPredicate的结果时。更重要的是让你的代码一开始就更简单。发出基本查询,首先没有过滤器。从那里构建。
    猜你喜欢
    • 1970-01-01
    • 2021-07-31
    • 1970-01-01
    • 2016-09-08
    • 2011-04-29
    • 1970-01-01
    • 2017-12-12
    • 2012-03-29
    • 1970-01-01
    相关资源
    最近更新 更多