JPA / EclipseLink:Joined WHERE查询没有给出预期的结果

时间:2014-04-27 22:52:04

标签: java java-ee jpa eclipselink

现在这非常令人困惑......我有一个引用实体Order的JPA实体User。用户可以是订单的买方或卖方。

由于买方和卖方都可以输入订单的其他信息,因此我将其移至额外的实体OrderUserData。可能存在也可能不存在相应的OrderUserData对象,但如果存在,则用户应该只能看到他们创建的条目(基于USER_ID),而不能看到另一方的条目。

实体看起来像这样:

@Entity
@Table(name = "T_ORDER")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "SELLER_ID")
    private User seller;

    @ManyToOne
    @JoinColumn(name = "BUYER_ID")
    private User buyer;

    @OneToMany(mappedBy = "order", fetch = FetchType.EAGER)
    private List<OrderUserData> userData = new ArrayList<>();

   //..
}

-

@Entity
@Table(name = "T_ORDER_USERDATA")
public class OrderUserData {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "ORDER_ID")
    private Order order;

    @ManyToOne
    @JoinColumn(name = "USER_ID")
    private User user;

    private String comment;
  //...
}

User不是很令人兴奋,只是ID和基本名称字段)

现在,当我尝试选择要在网站上显示的相应数据时,我遇到了一个问题:

String qry = "SELECT o FROM Order o LEFT JOIN o.userData ud "
            + " WHERE (o.seller.id = :userId OR o.buyer.id = :userId)"
            + " AND ( ud.user IS NULL OR ud.user.id = :userId )";
    TypedQuery<Order> query = em.createQuery(qry, Order.class);

    query.setParameter("userId", userId);

我们说我执行此操作,将userId设置为2:

我的数据库看起来像这样:

ORDER
=====
ID    SELLER_ID    BUYER_ID
1     1            2
2     2            3
3     3            1

ORDER_USERDATA
===============
ID     ORDER_ID    USER_ID    COMMENT
1      1           1          Comment that only user 1 should see
2      1           2          Comment that only user 2 should see

但与您期望的不同,在执行上述查询时,两个记录都包含在userData列表中!似乎JPA正在执行两个查询(尽管获取了EAGER)并忽略了第二个上的WHERE。这是为什么?还有什么其他解决方案,而不是循环遍历Java级别的userData列表,并踢出相应用户不应该看到的条目?

3 个答案:

答案 0 :(得分:2)

无法使用查询在 Order 对象中加载 OrderUserData 对象。也许你混淆了ORM功能,将数据库中的行映射到Java对象,具有查询功能 映射意味着1-1行和对象之间的对应关系,因此 Order 对象始终包含与 Order相关的每个 OrderUserData 行的所有 OrderUserData 对象行。 fetch类型只是一个加载策略,一旦加载了包含对象(EAGER)或一旦访问了包含的对象(LAZY),就确定在何时加载相关对象。(LAZY)。 您可以获取列表,使用适当的过滤器在 OrderUserData 对象上发出查询,并从每个对象中获取 Order 对象,即

  

SELECT ud FROM OrderUserData ud WHERE(ud.order.seller.id =:userId   或者ud.order.buyer.id =:userId)AND(ud.user IS NULL或ud.user.id =   :userId)

答案 1 :(得分:1)

您的查询似乎运行良好,因为它选择了正确的Order实体。然后JPA获取所选订单的所有OrderUserData子项:那是因为未过滤oneToMany连接。 我不认为可以使用eclipseLink(如Hibernate @FILTER)对预过滤的oneToMany进行建模,因此您应该删除它并仅映射orderUserDataId字段。然后,您可以在1个查询中获取实体,但它们不会被链接

SELECT o, ud FROM Order o, o.userData ud WHERE (o.seller.id = :userId OR o.buyer.id = :userId) AND ( ud.orderUserDataId = o.id and (ud.user IS NULL OR ud.user.id = :userId) )";

另一方面,如果其他用例需要oneToMany,那么您可以创建2个不同的Order实体:

  • 1&#34; OrderLight&#34;没有oneToMany
  • 1&#34; OrderFull&#34;使用从OrderLight派生的oneToMany。

答案 2 :(得分:0)

虽然user3580357和remigio已经正确回答了为什么这不起作用,但我是否可以建议您在数据库级别创建视图

类似的东西(可能需要根据您的需要或RDBMS进行调整):

CREATE OR REPLACE VIEW 
  ORDER_WITH_USERDATA 
AS 
  SELECT o.*, oud.* 
FROM ORDER o
LEFT JOIN ORDER_USERDATA oud
  ON o.id = oud.order_id

这基本上会为每个订单提供两个不同的“逻辑”记录。然后,您可以创建一个适用于此视图的其他JPA实体,并执行SELECT / WHERE ...而无需(LEFT)JOIN。