JPQL-Abfrage: Wie werden Zeilen nach einer Beziehung gefiltert?

Ich bin neu in JPA 2.0 und es gibt einige Dinge, die ich nicht verstehe.

Ich habe ein paar Tische:

<code>

    CUST table (for customers)
    --------------------------
    CUST_ID   (pk, integer)
    CUST_NAME (varchar)

</code>

und

<code>

    ORD table (for orders)
    ----------------------
    ORD_ID     (pk, integer)
    ORD_STATUS (char) can be: 'N' for new, 'S' for shipped, 'D' for delivered
    CUST_ID    (fk, integer)

</code>

Die Beziehung ist einfach "eins zu viele" (jeder Kunde kann viele Bestellungen aufgeben).

Inhalt der Tabellen:

<code>

    CUST_ID | CUST_NAME
    -------------------
     1      | elcaro
     2      | tfosorcim
     3      | elppa

</code>

und

<code>

    ORD_ID | ORD_STATUS | CUST_ID
    -----------------------------
     2     | N          | 1
     3     | N          | 1
     4     | N          | 1
     5     | S          | 1
     6     | S          | 1
     7     | D          | 1
     8     | D          | 1
     9     | D          | 1
     10    | D          | 2
     11    | N          | 2
     12    | S          | 3
     13    | S          | 3

</code>

So habe ich meine Klassen kommentiert:

<code>

    @Entity(name = "Customer")
    @Table(name = "CUST")
    public class Customer implements Serializable
    {
        private static final long serialVersionUID = 1L;

        @Id
        @Column(name = "CUST_ID")
        private Integer id;

        @Column(name = "CUST_NAME")
        private String name;

        @OneToMany(mappedBy = "customer")
        private List<Order> orders;

        // Default constructor, getters and setters (no annotations on these)
    }

</code>

und

<code>

    @Entity(name = "Order")
    @Table(name = "ORD")
    public class Order implements Serializable
    {
        private static final long serialVersionUID = 1L;

        @Id
        @Column(name = "ORD_ID")
        private Integer id;

        @Column(name = "ORD_STATUS")
        private Character status;

        @ManyToOne
        @JoinColumns
        (
          {
            @JoinColumn(name = "CUST_ID", referencedColumnName = "CUST_ID")
          }
        )
        private Customer customer;

        // Default constructor, getters and setters (no annotations on these)
    }

</code>

Alles funktioniert einwandfrei, die folgende JPQL-Abfrage liefert die erwarteten Ergebnisse:

<code>`select c from Customer c`</code>

Es werden drei Objekte vom Typ Kunde zurückgegeben, von denen jedes die Aufträge enthält, die zu diesem Kunden gehören.

Aber jetzt möchte ich die Liste der Kunden extrahieren, die Bestellungen im Status 'N' haben, zusammen mit den zugehörigen Bestellungen (natürlich nur die Bestellungen im Status 'N'). Früher hätte ich eine SQL-Abfrage wie diese geschrieben:

<code>

    select      c.cust_id,
                c.cust_name,
                o.ord_id,
                o.ord_status
    from        cust c
    inner join  ord o on (o.cust_id = c.cust_id)
    where       o.ord_status = 'N'

</code>

und es hätte die folgende Ergebnismenge zurückgegeben:

<code>

    CUST_ID | CUST_NAME | ORD_ID | ORD_STATUS
    -----------------------------------------
     1      | elcaro    | 2      | N
     1      | elcaro    | 3      | N
     1      | elcaro    | 4      | N
     2      | tfosorcim | 11     | N

</code>

Die folgende JPQL-Abfrage liefert jedoch nicht die erwarteten Ergebnisse:

<code>`select distinct c from Customer c join c.orders o where o.status = 'N'`</code>

Es gibt die richtige Menge von Kunden zurück (Kunde 'elppa' hat keinen Status 'N' Bestellung und ist korrekt ausgeschlossen), aber jeder Kunde enthält die vollständige Menge von Bestellungen, unabhängig vom Status. Anscheinend wird die Where-Klausel nur ausgewertet, um zu bestimmen, welche Kundengruppe extrahiert werden muss, und dann beginnt der Persistenzanbieter, in der Beziehung zu navigieren, um die gesamte Bestellmenge zu extrahieren. Wenn ich ein wenig darüber nachdenke, muss ich zugeben, dass es Sinn macht.

Ich habe dann eine andere JPQL-Abfrage ausprobiert:

<code>`select c, o from Customer c join c.orders o where o.status = 'N'`</code>

Diese JPQL-Abfrage liefert ähnliche Ergebnisse wie die vorherige SQL-Abfrage: Jedes Ergebnis (4 Ergebnisse wie erwartet) ist ein Array mit zwei Objekten, das erste Objekt hat den Typ Customer und das zweite Objekt den Typ Order. Aber auch hier enthalten die Objekte des Typs "Kunde" den vollständigen Satz verwandter Aufträge (diesmal wie erwartet). Ganz zu schweigen von der Tatsache, dass die Bestellungen jetzt nicht in den Kundenobjekten enthalten sind, sondern wie in einer SQL-Ergebnismenge separat zurückgegeben werden.

Die Frage ist nun: Ist es möglich, eine JPQL-Abfrage zu schreiben, die nicht nur die Kunden herausfiltert, die keine Bestellung im Status 'N' haben, sondern auch die zugehörigen Bestellungen (die während der Beziehungsnavigation abgerufen werden), die nicht im Status 'sind? N auch? Was ich bekommen möchte, ist ein 2-Kunden-Ergebnis, bei dem jeder Kunde nur seinen Status 'N' Bestellungen enthält.

Ich habe das Java EE 6 Tutorial gelesen und eines der Beispiele (die Bestellanwendung) hat ein ähnliches Schema wie ich, aber ich konnte keine solche Abfrage finden (im heruntergeladenen Quellcode).

Obwohl ich der Meinung bin, dass dies das Standardverhalten ist, verwende ich einen Oracle Weblogic 12c-Server (über seinen Eclipse-Adapter) und der Persistenzanbieter scheint EclipseLink zu sein.

Danke im Voraus.

Freundliche Grüße,

Stefano

Antworten auf die Frage(1)

Ihre Antwort auf die Frage