JPQL, Join, Straßen

Hallöchen :slight_smile:

Mal angenommen ich habe folgende Tabellen in einer Access-Datenbank:

Tabelle 1: Stadtnummer, Stadtname, Stadtbürgermeister

Tabelle 2: Stadtnummer1 (Quelle), Stadtnummer2 (Ziel), Straßennummer, Straßenname, Straßenbelag

Also im Prinzip welche Straße von A nach B führt :smiley:

Wenn ich jetzt alle Städte und alle Straßen auslese (ich habe jeweils die Klasse Stadt und die Klasse Strasse mit den entsprechenden Eigenschaften), funktioniert das wunderbar.

Nun will ich aber auch einfach den Namen einer Stadt eingeben können (in einen String) und anhand dieses Strings zunächst natürlich die gesuchte Stadt raussuchen und dann aber auch alle mit ihr verbundenen Städte (zunächst erstmal nur die Städte an sich).

Dafür habe ich momentan folgende Abfrage für’s Query (wobei searchedCityName der abgefrage Stadtname ist):

              "SELECT sta FROM Staedte sta LEFT JOIN FETCH Strassen str WHERE sta.Stadtname = :"
                      + searchedCityName + " AND str.Stadtnummer1.Stadtname = :" + searchedCityName
                      + " OR str.Stadtnummer2.Stadtname = :" + searchedCityName, Stadt.class);```

Diese Abfrage funktioniert allerdings nicht. Seht ihr da vielleicht auf Anhieb den Fehler oder habt einen Tipp für mich? :)

LG, die kleine, unwissende black_droid :D

ist das dein erster Versuch dazu?
oder hast du schon "SELECT sta FROM Staedte sta WHERE sta.Stadtname = :" + searchedCityName für sich getestet,
und dann ja wohl hoffentlich erfolgreich? anderenfalls dazu zunächst Probleme zu besprechen…

der Doppelpunkt verwirrt mich, auch wenn ich JPQL direkt nicht kenne,
soll das sowas werden? : http://www.objectdb.com/java/jpa/query/parameter

dann musst du aber auch "SELECT sta FROM Staedte sta WHERE sta.Stadtname = :searchCityName"
schreiben und später erst den Wert für benannten Parameter übergeben

direkt ins SQL reinschreiben geht notfalls auch, aber ohne Doppelpunkt, dafür mit Anführungszeichen,
und Gefahr dass dir jemand beliebiges SQL reinzaubert


einzelne Städte zu laden wäre schon Ziel genug,
für das andere sei zunächst angemerkt, dass man nicht AND und OR ohne Klammern beliebig kombinieren sollte

und meine Vorstellung eines Joins wäre:
[sql]SELECT sta FROM Staedte sta, Strassen strA, Strassen strB, Staedte staA, Staedte staB,
WHERE sta = strA.Stadtnummer1 and sta = strB.Stadtnummer2
staA = strA.Stadtnummer2 and staB = strB.Stadtnummer1
and (sta.name = :s or staA.name = :s or staB.name = :s)[/sql]
die ersten drei Zeilen bauen ein Gebilde auf, sta mit 2x Straßen verbinden,
nämlich einmal über Stadtnummer1 der Straßen (A), und einmal über Stadtnummer2 (B), das ist getrennt zu machen,
dazu dann noch die jeweils weitere Stadt als eigene Enitäten dazuholen, verbinden,

in der letzten Zeilen das Mega-Konstrukt dann auf interessante Straßen eingrenzen
ungetestet :wink:
nötiges Einsetzen der Stadtnamen-Attribute für die Vergleiche statt Objekte und Problem mit fehlenden Einträgen ergo Left Joint usw. habe ich auch nicht betrachtet

einfacher wäre es mit einer Straßen-Tabelle, die jede Verbindung zweimal ablegt,
dann reicht 1 Join auf Straße (+ weiter Nachbarstadt, nur A statt hier A + B)


sofern die Wahl, DB-Tabellen übrigens besser immer in Einzahl benennen!: Stadt, Straß(ss)e (im Link Country, nicht Countries…) ,
genau wie die Java-Klassen Stadt & Co.

[QUOTE=SlaterB]ist das dein erster Versuch dazu?
oder hast du schon "SELECT sta FROM Staedte sta WHERE sta.Stadtname = :" + searchedCityName für sich getestet,
und dann ja wohl hoffentlich erfolgreich? anderenfalls dazu zunächst Probleme zu besprechen…[/QUOTE]

Danke für den Hinweis, denn wenn ich das alleine teste, bekomme ich folgende Exception:

Exception in thread “main” java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property:…

Ich habe das jetzt auch schon auf einen Parameter umgebaut:

      query.setParameter("searchedCityName", searchedCityName).getResultList();```

Jetzt frage ich mich natürlich, was die Exception verursacht :/

Denn Doppelpunkt brauchst du NUR, wenn du mit query.setParameter arbeitest (sonst nicht, sonst musst du den richtigen Literal einfügen, d.h. mit einfachen Anführungszeichen usw.)

Aber vergiss das mit String+ und direktem zusammenstückeln, arbeite immer mit setParameter. Was kommt denn bei deiner zweiten Variante als Fehlermeldung? Die gleiche?

Ist “Stadtname” wirklich der richtige Name der Property in der Klasse Stadt?

[QUOTE=Bleiglanz]
Ist „Stadtname“ wirklich der richtige Name der Property in der Klasse Stadt?[/QUOTE]

Okay, es hat am Namen gelegen, danke, jetzt funktioniert zumindest die einfach Abfrage, bei der ich nur erstmal den Namen suche :smiley: (Ich hab das Beispiel hier auf Grundlage einer realen Aufgabenstellung aufgebaut, von der der Original-Aufbau nicht unbedingt öffentlich rumschwirren muss :x)

bist ja schon weitergekommen, aber hier lohnt sich betonender Hinweis:

dies ist allgemein eine brauchbare Fehlermeldung (gibt ganz andere, richtig…) und das was du als … wegläßt ist genau eine wichtige Info:
welches Attribut welcher Klasse oder DB-Tabelle wird nicht erkannt?
und dann ist das eben meist ein Fehler

Ok, dann gibt’s jetzt 'ne vollständige Fehlermeldung :smiley:

Für die Abfrage:

                      "SELECT sta FROM Staedte sta, Strassen str WHERE sta.Stadtname = :searchedCityName AND (str.Stadtnummer1.Stadtname = :searchedCityName OR str.Stadtnummer2.Stadtname = :searchedCityName)", Stadt.class);```

erhalte ich folgende Fehlermeldung:

```Exception in thread "main" java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: Stadtname of: Strassen [SELECT sta FROM Staedte sta, Strassen str WHERE sta.Stadtname = :searchedCityName AND (str.Stadtnummer1.Stadtname = :searchedCityName OR str.Stadtnummer2.Stadtname = :searchedCityName)]
	at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1376)
	at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1317)
	at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:336)
	at de.ullapopken.schnittstellen_karte.Datenbankzugriff.readSystems(Datenbankzugriff.java:45)
	at de.ullapopken.schnittstellen_karte.Generator.generateMap(Generator.java:31)
	at de.ullapopken.schnittstellen_karte.Program.main(Program.java:12)
Caused by: org.hibernate.QueryException: could not resolve property: Stadtname of: Strassen [SELECT sta FROM Staedte sta, Strassen str WHERE sta.Stadtname = :searchedCityName AND (str.Stadtnummer1.Stadtname = :searchedCityName OR str.Stadtnummer2.Stadtname = :searchedCityName)]
	at org.hibernate.persister.entity.AbstractPropertyMapping.propertyException(AbstractPropertyMapping.java:81)
	at org.hibernate.persister.entity.AbstractPropertyMapping.toType(AbstractPropertyMapping.java:75)
	at org.hibernate.persister.entity.AbstractEntityPersister.toType(AbstractEntityPersister.java:1465)
	at org.hibernate.hql.ast.tree.FromElementType.getPropertyType(FromElementType.java:315)
	at org.hibernate.hql.ast.tree.FromElement.getPropertyType(FromElement.java:487)
	at org.hibernate.hql.ast.tree.DotNode.getDataType(DotNode.java:611)
	at org.hibernate.hql.ast.tree.DotNode.prepareLhs(DotNode.java:263)
	at org.hibernate.hql.ast.tree.DotNode.resolve(DotNode.java:210)
	at org.hibernate.hql.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:117)
	at org.hibernate.hql.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:113)
	at org.hibernate.hql.ast.HqlSqlWalker.resolve(HqlSqlWalker.java:880)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.expr(HqlSqlBaseWalker.java:1330)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.exprOrSubquery(HqlSqlBaseWalker.java:4471)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.comparisonExpr(HqlSqlBaseWalker.java:3944)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2047)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:1997)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:1975)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:831)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:617)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:301)
	at org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:244)
	at org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:256)
	at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:187)
	at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:138)
	at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
	at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
	at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:124)
	at org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:156)
	at org.hibernate.impl.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:135)
	at org.hibernate.impl.SessionImpl.createQuery(SessionImpl.java:1770)
	at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:287)```

Was will er mir mit dem "Could not resolve property" sagen? :confused:

Na, der kann
[SQL]
str.Stadtnummer1.Stadtname
[/SQL]
nicht auflösen. Wie sieht denn dein Mapping aus?

tja, verständlich wäre die Fehlermeldung für mich, falls Stadtnummer1/ Stadtnummer2 von Strassen zufällig selber Strassen sind, aber das ist wohl nicht der Fall :wink:
evl. ist es auch noch irgendeine allgemeine Schwierigkeit mit der Strassen-Klasse, hast du die schon einmal erfolgreich in einer Query benutzt?

wenn es so sehr nach HQL/ Hibernate ausschaut, dann möchte ich nach Möglichkeit die beiden Klassen dazu mal sehen,
und an allgemeine Java-Grundlagen erinnern:
Attribute von Klassen immer mit Kleinbuchstaben beginnen, getter + setter nach festen Schema, das spielt eigentlich im HQL/ Hibernate alles zusammen:

Beispiel:
DB-Tabelle DB_Stadt
Java-Klasse class Stadt { private String name; public String getName() public String setName(String name) }
HQL select name from Stadt sta where sta.name = :name

bis auf Klassennamen, nach Beliebigkeit Schlüsselwörter wie Select und nach Beliebigkeit evtl. :- Parameter sollte kein Wort in HQL mit einem Großbuchstaben beginnen

Ich hab’s geschafft \o/

Folgendes funktioniert jetzt bei mir:

              .createQuery(
                      "SELECT DISTINCT sta FROM Staedte sta, Strassen str, Staedte sta2 "
                              + "WHERE sta.stadtname = :searchedCityName "
                              + "OR (sta2.stadtname = :searchedCityName AND str.stadtnummer1 = sta2.stadtnummer AND str.stadtnummer2 = sta.stadtnummer) "
                              + "OR (sta2.stadtname = :searchedCityName AND str.stadtnummer1 = sta.stadtnummer AND str.stadtnummer2 = sta2.stadtnummer)",
                      System.class);```

Außerdem hatte ich die Relationship-Annotations vergessen ;)

Viele Dank für eure Hilfe und die Hinweise :)

leicht fraglich was die DB daraus macht, ohne direkte Join-Bedingungen vielleicht alle Einträge beliebig untereinander kombiniert,
überschaubare 10 Städte + 100 Straßen -> bereits 10 x 100 x 10 = 10.000 Einträge und dann erst die OR-Bedingungen prüfen

zu meiner Query (falls korrekt) ist es dann ja nicht mehr weit, da wären es evtl. nur 100 Einträge, die 100 Straßen mit ihren jeweiligen Städten verknüpft,
und dann OR, in dem Fall freilich aufzupassen ob auch Städte ohne Straßen gesucht werden, kaum dabei
(edit: alle Straßen einer Stadt jeweils noch untereinander gekreuzt, auch nicht schön…)