当前位置: 首页 > 知识库问答 >
问题:

“不在具有JPA条件API的`subquery`”语句中

段干楚青
2023-03-14

我在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)
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;
}
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)
               ;
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 )

共有1个答案

雍飞雨
2023-03-14

我就是这样修复问题的:我主要选择了子查询中的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会使条件查询的“类型安全”特性稍稍松动一些。

 类似资料:
  • 有什么想法吗? 多谢了。

  • 问题内容: 我有一个变量。 我想呼应,如果等于任何下列值,,,,或。有没有办法通过像?? 这样的单个语句来做到这一点? 问题答案: 我认为,使用“ Or”代替“ And”会有所帮助

  • 我一定错过了什么。我正在计算一个列表,想跳过特定的数字,所以我做了一个switch语句: 以下是输出: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 22 23 24 25 26 27 28 29 30 32 33 34 35 36 37 38 39 40 41 42 44 45 46 47 48 50 52 53 注意,44出现了,尽管我没

  • 跟其它程序设计语言一样,Bash中的条件语句让我们可以决定一个操作是否被执行。结果取决于一个包在[[ ]]里的表达式。 条件表达式可以包含&&和||运算符,分别对应 与 和 或 。除此之外还有很多有用的表达式。 共有两个不同的条件表达式:if和case。 基元和组合表达式 由[[ ]](sh中是[ ])包起来的表达式被称作 检测命令 或 基元。这些表达式帮助我们检测一个条件的结果。在下面的表里,为

  • 条件语句体应该总是被大括号包围。尽管有时候你可以不使用大括号(比如,条件语句体只有一行内容),但是这样做会带来问题隐患。比如,增加一行代码时,你可能会误以为它是 if 语句体里面的。此外,更危险的是,如果把 if 后面的那行代码注释掉,之后的一行代码会成为 if 语句里的代码。 推荐: if (!error) { return success; } 不推荐: if (!error)

  • Jade 条件语句和使用了(-) 前缀的JavaScript语句是一致的,然后它允许你不使用圆括号,这样会看上去对设计师更友好一点, 同时要在心里记住这个表达式渲染出的是_常规_Javascript: for user in users if user.role == 'admin' p #{user.name} is an admin else p= user.name