我有一个 Hibernate 实体,与另一个实体具有可选关系:
@NamedEntityGraph(
name = "expense-for-frontend",
attributeNodes = {
@NamedAttributeNode("shipment"),
})
@Entity
public class Expense {
...
@ManyToOne(fetch = FetchType.LAZY, optional = true)
private Shipment shipment;
...
}
我希望能够加载
Expense
,无论他们是否有 Shipment
,但如果他们确实有货物,则应该预先加载回来,以避免数据库往返。存储库有一个类似于以下内容的查询:
@EntityGraph("expense-for-frontend")
@Query(
"""
SELECT e
FROM Expense e
LEFT JOIN Shipment s ON e.shipment.id = s.id
WHERE (:status IS NULL OR e.status = :status)
""")
List<PaidShipmentExpenseBE> findAllFilteredShipmentExpensesWithPayment(
@Param("status") @Nullable VendorPaymentStatus status);
执行的SQL是:
select eb1_0.id, ...
from expense eb1_0
join shipment s1_0 on s1_0.id=eb1_0.shipment_id
left join shipment sb1_0 on sb1_0.tenant_id = ? and eb1_0.shipment_id=sb1_0.id
...
针对
shipment
的内连接由 @NamedAttributeNode("shipment")
中的 EntityGraph
添加,并且 s1_0 在查询的其余部分中不使用。因为它添加了内部联接,所以在使用该功能时我无法检索任何未链接到发货的费用EntityGraph
。
我找不到任何方法来更改 EntityGraph 以允许可选的多对一关系。
@NamedAttributeNode("shipment")
,因为这会导致每行费用有额外的数据库往返,而且费用有很多。NamedAttributeNode
来告诉它生成左连接。这似乎是一种非常常见的情况,并且列的可为空性是通过标准 jakarta 持久性注释
@ManyToOne(fetch = FetchType.LAZY, optional = true)
公开的,因此我假设 EntityGraph
可以正确处理这种情况。我是不是错过了什么?
您的查询有误。最有可能的是,您想写
e.shipment.id = s.id
,而不是 e.shipment_id = s.id
,但这并不重要,
因为在您的查询中,关系 LEFT JOIN Shipment s ON e.shipment.id = s.id
是多余的。您已经使用 @ManyToOne
注释描述了 Expense 和 Shipment 实体之间的关系,因此 Hibernate 进行了第一个连接。通过向查询添加另一个关系,您迫使 Hibernate 进行另一个连接。但为了避免在 sb1_0.id
表达式中访问 NULL,它必须使用内连接而不是通常的左连接来获取
sb1_0
。因此,只需从查询中删除 LEFT JOIN Shipment s ON e.shipment.id = s.id
即可。
另外,在
@ManyToOne(fetch = FetchType.LAZY, optional = true)
表达式中,optinal默认为true,所以不需要指定。