【问题标题】:"NOT IN `subquery`" statement with JPA Criteria API使用 JPA Criteria API 的“NOT IN `subquery`”语句
【发布时间】:2014-08-02 17:55:04
【问题描述】:

我在我的 Java EE 应用程序中使用 JPA 和 Criteria API 来查询我的数据库 (PostgreSQL)。 我将树实现为闭包表,并试图获取根节点。 这是我的架构(省略了无用的字段):

NeedsTreev2     :
id              | primary key

NeedNode        :
id              | primary key
needstree_id    | foreign key references needstreev2(id)

NeedLink        :
ancestor_id     | foreign key references neednode(id)
descendant_id   | foreign key references neednode(id)
needstree_id    | foreign key references needstreev2(id)

这是我的 JPA 映射

public class NeedsTreev2 {
    @Id
    private Long id;
}

public class NeedNode {
    @Id
    private Long id;
}

public class NeedLink {
    @ManyToOne
    private NeedNode ancestor;
    @ManyToOne
    private NeedNode descendant;
    @ManyToOne
    private NeedsTreev2;
}

树的根节点是那些从不用作后代的节点,所以 这是返回指定树的根节点的 SQL 查询:

SELECT nNode.* FROM neednode nNode
               INNER JOIN needstreev2 nTree
                       ON nNode.needstree_id = nTree.id

               WHERE nTree.id = ?
                 AND nNode.id NOT IN
                (SELECT nLink.descendant_id FROM needlink nLink
                                            WHERE nLink.ancestor_id != nLink.descendant_id)
               ;

然后我尝试用 Criteria 翻译它:

public List<NeedNode> getRootsByTree(NeedsTreev2 tree) {
        List<NeedNode> ret;

        CriteriaBuilder cb = this.getEntityManager().getCriteriaBuilder();
        CriteriaQuery<NeedNode> cq = cb.createQuery(NeedNode.class);

        Root<NeedNode> nNode = cq.from(NeedNode.class);

        /* Here we define the subquery */
        Subquery<NeedNode> sq = cq.subquery(NeedNode.class);
        Root<NeedLink> nLink = sq.from(NeedLink.class);
        sq.where(cb.notEqual(nLink.get(NeedLink_.ancestor), nLink.get(NeedLink_.descendant)));
        sq.select(nLink.get(NeedLink_.descendant));
        /* End of subquery */

        Predicate[] p = {
            cb.equal(nNode.get(NeedNode_.needsTree), tree),
            cb.not(cb.in(nNode).value(sq)) /* This is where the problem occurs */
        };

        cq.where(cb.and(p));

        TypedQuery<NeedNode> query = this.getEntityManager().createQuery(cq);
        ret = query.getResultList();

        return (ret);
    }

这段代码对我来说似乎是合乎逻辑的,但它会引发异常:

org.eclipse.persistence.exceptions.QueryException
Exception Description: Illegal use of getField() [NEEDNODE.ID] in expression.
Query: ReadAllQuery(referenceClass=NeedNode )

我也尝试将cb.not(cb.in(nNode).value(sq)) 替换为cb.not(nNode.in(sq)),但它引发了同样的异常。

我可能错过了一些东西,但我找不到它。 感谢您的帮助。

【问题讨论】:

    标签: java jakarta-ee jpa eclipselink criteria-api


    【解决方案1】:

    这就是我解决问题的方法:我主要在子查询中选择了NeedNode.id,而不是完整的对象。这样,IN 语句就起作用了。

    public List<NeedNode> getRootsByTree(NeedsTreev2 tree) {
        List<NeedNode> ret;
    
        CriteriaBuilder cb = this.getEntityManager().getCriteriaBuilder();
        CriteriaQuery<NeedNode> cq = cb.createQuery(NeedNode.class);
    
        Root<NeedNode> nNode = cq.from(NeedNode.class);
    
        Subquery<Long> sq = cq.subquery(Long.class);
        Root<NeedLink> nLink = sq.from(NeedLink.class);
        Join<NeedLink, NeedNode> d = nLink.join(NeedLink_.descendant, JoinType.INNER);
        sq.where(cb.notEqual(nLink.get(NeedLink_.ancestor), nLink.get(NeedLink_.descendant)));
        sq.select(d.get(NeedNode_.id));
        sq.distinct(true);
    
        Predicate[] p = {
            cb.equal(nNode.get(NeedNode_.needsTree), tree),
            nNode.get(NeedNode_.id).in(sq).not()
        };
    
        cq.where(cb.and(p));
    
        TypedQuery<NeedNode> query = this.getEntityManager().createQuery(cq);
        ret = query.getResultList();
    
        return (ret);
    }
    

    所以代码需要一个Join 变量和返回Long 而不是NeedNode 的子查询。它以这种方式工作,但我不明白为什么它不像问题中所写的那样工作。 IMO,使子查询选择 ids 使 Criteria 查询失去了一点其“类型安全”功能。

    【讨论】:

    • 真正有用的几行:Join&lt;NeedLink, NeedNode&gt; d = nLink.join(NeedLink_.descendant, JoinType.INNER); sq.where(cb.notEqual(nLink.get(NeedLink_.ancestor), nLink.get(NeedLink_.descendant))); sq.select(d.get(NeedNode_.id));
    猜你喜欢
    • 2013-11-02
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多