Java JPA/EclipseLink:在查询未给出预期结果的地方加入

Java JPA/EclipseLink:在查询未给出预期结果的地方加入,java,jakarta-ee,jpa,eclipselink,Java,Jakarta Ee,Jpa,Eclipselink,现在这很令人困惑。。。我有一个JPA实体订单,它引用一个实体用户。用户可以是订单的买方或卖方 因为买方和卖方都可以为订单输入附加信息,所以我将其移动到了一个额外的实体OrderUserData。可能存在也可能不存在相应的OrderUserData对象,但如果存在,用户应该只能看到他们创建的条目(基于用户ID),而不能看到另一方的条目 实体如下所示: @Entity @Table(name = "T_ORDER") public class Order { @Id @Genera

现在这很令人困惑。。。我有一个JPA实体
订单
,它引用一个实体
用户
。用户可以是订单的买方或卖方

因为买方和卖方都可以为订单输入附加信息,所以我将其移动到了一个额外的实体
OrderUserData
。可能存在也可能不存在相应的
OrderUserData
对象,但如果存在,用户应该只能看到他们创建的条目(基于用户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<>();

   //..
}
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
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);

但与您预期的不同,在执行上述查询时,这两条记录都包含在
userData
列表中!看起来JPA正在执行两个查询(尽管有急切的获取),而忽略了第二个查询的WHERE。为什么呢?除了在Java级别上循环查看
userData
列表并删除相应用户不应该看到的条目之外,还有什么其他解决方案呢?

您的查询似乎运行良好,因为它正确地选择了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,则可以创建两个不同的订单实体:

  • 1“OrderLight”不带oneToMany
  • 1“OrderFull”和oneToMany,派生自OrderLight
无法使用查询在Order对象中加载OrderUserData对象。也许您混淆了ORM功能(将数据库中的行映射到Java对象)和查询功能。
映射意味着行和对象之间的1-1对应关系,因此订单对象始终包含与订单行相关的每个OrderUserData行的所有OrderUserData对象。 提取类型只是一种加载策略,确定在何时提取相关对象,只要加载了包含的对象(急切)或访问了包含的对象(惰性)。
您可以通过使用适当的过滤器对OrderUserData对象发出查询并从每个对象获取订单对象,即

从OrderUserData ud中选择ud,其中(ud.order.seller.id=:userId 或ud.order.buyer.id=:userId)和(ud.user为NULL或ud.user.id= :userId)


虽然user3580357和remigio已经给出了这不起作用的正确答案,但我建议您在数据库级别创建一个视图

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


这将为每个订单提供两个不同的“逻辑”记录。然后,您可以创建一个在该视图上工作的附加JPA实体,并执行SELECT/WHERE。。。根本不需要(左)加入。

Thx,这真的很有效!我唯一不明白的是为什么?:)
WHERE(ud.order.seller.id=:userId…
这不是一回事吗?这是从OrderUserData中进行选择,同时对不同的实体(即附加到OrderUserData的订单)应用WHERE条件。那么,为什么从ud=>order进行遍历而不是从另一个方向进行遍历时它会起作用呢?
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) )";
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