现在这非常令人困惑......我有一个引用实体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
列表,并踢出相应用户不应该看到的条目?
答案 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实体:
答案 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。