Consulta JPQL: como filtrar linhas em um relacionamento?
Sou novo no JPA 2.0 e há poucas coisas que não entendo.
Eu tenho algumas tabelas:
<code> CUST table (for customers) -------------------------- CUST_ID (pk, integer) CUST_NAME (varchar) </code>
e
<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>
O relacionamento é um simples "um para muitos" (cada cliente pode fazer muitos pedidos).
Conteúdo das tabelas:
<code> CUST_ID | CUST_NAME ------------------- 1 | elcaro 2 | tfosorcim 3 | elppa </code>
e
<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>
Veja como anotei minhas aulas:
<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>
e
<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>
Tudo funciona bem, a seguinte consulta JPQL produz os resultados que eu esperava:
<code>`select c from Customer c`</code>
ele retorna três objetos do tipo Customer, cada um contendo os pedidos que pertencem a esse cliente.
Mas agora, quero extrair a lista de clientes que têm pedidos no status 'N', juntamente com os pedidos associados (somente os pedidos de status 'N', é claro). De volta aos bons tempos, eu teria escrito uma consulta SQL como esta:
<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>
e teria retornado o seguinte conjunto de resultados:
<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>
A consulta JPQL a seguir, no entanto, não produz os resultados esperados:
<code>`select distinct c from Customer c join c.orders o where o.status = 'N'`</code>
ele retorna o conjunto correto de clientes (o cliente 'elppa' não possui nenhum status 'N' e está corretamente excluído), mas cada cliente contém o conjunto completo de pedidos, independentemente do status. Parece que a cláusula 'where' é avaliada apenas para determinar qual conjunto de clientes deve ser extraído e, em seguida, o provedor de persistência começa a navegar no relacionamento para extrair o conjunto completo de pedidos. Pensando um pouco sobre isso, devo admitir que faz sentido.
Eu então tentei outra consulta JPQL:
<code>`select c, o from Customer c join c.orders o where o.status = 'N'`</code>
essa consulta JPQL produz resultados semelhantes aos produzidos pela consulta SQL anterior: cada resultado (4 resultados como esperado) é uma matriz de 2 objetos, o primeiro objeto é do tipo Customer e o segundo é do tipo Order. Mas, novamente, os objetos do tipo Customer contêm o conjunto completo de pedidos relacionados (como eu esperava, desta vez). Sem mencionar o fato de que agora os pedidos não estão contidos nos objetos Customer, mas são retornados separadamente, assim como em um conjunto de resultados SQL.
Agora a pergunta é: é possível escrever uma consulta JPQL que filtra, não apenas os clientes que não têm um pedido no status 'N', mas os pedidos relacionados (buscados durante a navegação de relacionamento) que não estão no status ' N 'também? O que eu gostaria de conseguir é um resultado de 2 clientes, em que cada cliente contém apenas as ordens de status 'N'.
Eu li o Java EE 6 Tutorial e um dos exemplos (o Order Application) tem um esquema que é semelhante ao meu, mas não consegui encontrar uma consulta como essa (no código-fonte baixado).
Embora eu ache que o comportamento acima seja padrão, uso um servidor Oracle Weblogic 12c (por meio de seu adaptador Eclipse) e o provedor de persistência parece ser o EclipseLink.
Desde já, obrigado.
Cumprimentos,
Stefano