Und*_*nda 2 java jpa eclipselink java-ee criteria-api
我在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)
Run Code Online (Sandbox Code Playgroud)
这是我的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;
}
Run Code Online (Sandbox Code Playgroud)
树的根节点是从不用作后代的根节点,因此这是返回指定树的根节点的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)
;
Run Code Online (Sandbox Code Playgroud)
然后我尝试用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);
}
Run Code Online (Sandbox Code Playgroud)
这段代码对我来说似乎合乎逻辑,但是会引发异常:
org.eclipse.persistence.exceptions.QueryException
Exception Description: Illegal use of getField() [NEEDNODE.ID] in expression.
Query: ReadAllQuery(referenceClass=NeedNode )
Run Code Online (Sandbox Code Playgroud)
我也尝试替换为cb.not(cb.in(nNode).value(sq)),cb.not(nNode.in(sq))但是它引发了相同的异常。
我可能错过了一些东西,但找不到。谢谢您的帮助。
这是解决问题的方法:我主要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);
}
Run Code Online (Sandbox Code Playgroud)
因此,代码需要一个Join变量和子查询来Long代替NeedNode。尽管我不明白为什么它不如问题中所写的那样起作用,但它仍以这种方式工作。IMO,使子查询选择ID,使Criteria查询失去了“类型安全”功能。
| 归档时间: |
|
| 查看次数: |
11091 次 |
| 最近记录: |