"NICHT IN `Unterabfrage`" - Anweisung mit JPA Criteria API
Ich bin mit JPA in die Java EE Anwendung und die Kriterien API zur Abfrage meiner Datenbank (PostgreSQL).
Implementiert habe ich einen Baum als Verschluss-Tisch und ich versuche, den root-Knoten.
Hier mein schema (nutzlos Felder weggelassen):
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)
Hier ist meine JPA-mapping
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;
}
Den root-Knoten des Baumes sind jene, die nie als Nachkommen, so
hier ist die SQL-Abfrage liefert den Wurzel-Knoten des angegebenen Baum :
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)
;
Dann habe ich versucht zu übersetzen, mit den Kriterien :
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);
}
Dieser code scheint logisch für mich, aber es gibt eine Ausnahme :
org.eclipse.persistence.exceptions.QueryException
Exception Description: Illegal use of getField() [NEEDNODE.ID] in expression.
Query: ReadAllQuery(referenceClass=NeedNode )
Ich habe auch versucht zu ersetzen cb.not(cb.in(nNode).value(sq))
durch cb.not(nNode.in(sq))
aber es wirft die gleiche Ausnahme.
Ich wohl etwas verpasst, aber ich kann es nicht finden.
Vielen Dank für die Hilfe.
InformationsquelleAutor Unda | 2014-06-12
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dies ist, wie ich das problem behoben : habe ich vor allem ausgewählt, die
NeedNode.id
in der Unterabfrage statt des gesamten Objekts. Dieser Weg, derIN
- Anweisung funktioniert.So muss der code ein
Join
variable und die Unterabfrage zurückLong
stattNeedNode
. Es funktioniert so, obwohl ich nicht verstehe, warum es nicht funktioniert, als es ist geschrieben in der Frage. IMO, machen die Unterabfrage select-ids macht die Kriterien der Abfrage Locker ein kleines bisschen von seiner "Type-safe" - Funktion.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));
InformationsquelleAutor Unda