0

我有一个与发票实体具有 OneToMany 关系的客户实体。

在普通的旧 sql 中,我可以执行“从客户中选择客户名称、客户年龄、[其他一些字段]、发票在哪里……[在此处进行一些过滤]”,这让我得到了一条包含我需要的字段的准确记录。

在 JPA 中,我使用“从客户 c 中选择 c 加入 c.invoiceCollection where ... [与上述相同的过滤]”

这行得通,但我得到了 Customer 实体及其所有关联的发票。这是胡说八道,因为我从数据库中提取了大量(发票)数据,而我不需要这些数据。我只需要我的客户数据和一张发票,正如 where 子句中指定的那样。

更糟糕的是,我必须遍历 Customer.invoiceCollection 才能找到所需的一张特定发票。这花费了我更多的时间,而且它把我的“where”子句暴露给了中间层。

问题:如 where 子句中定义的那样,是否有 JPA 选择语法从一对多关系中准确获取一条记录?

到目前为止尝试过的事情:a)延迟加载。这不起作用,每当我尝试访问 Customer.invoiceCollection 时都会引发异常。即使它有效,我也会得到一个包含大约 1000 个条目的集合,这是我不需要的。

b) 将我的 jpa 语句更改为“select c,i from Customer c join c.invoiceCollection i where ...”。这将返回一个对象数组,我必须手动将其映射到客户/发票实体。它有效,但它使 ORM 哲学过时了。如果我在我的代码中手动执行从关系数据库记录/字段到 java 对象的所有映射,为什么我需要 JPA?

4

3 回答 3

1

这是关于 JPA 的最令人愤怒的事情之一。例如,如果您希望客户级联删除发票,则需要 OneToMany 端。在大多数情况下,您希望在删除客户时告诉 Invoices 将其自身删除,这样客户就不必知道 Invoice。

我对您的建议是您将 OneToMany 保留在那里,但让延迟加载正常工作。在您的代码中,不要直接访问“Customer#getInvoices”(除非您真的需要所有这些)。

这将允许您在不加载发票的情况下对加入发票的客户进行查询。

我猜你得到的例外只是与可以轻松修复的事务边界有关。

对于很多这样的关系,我经常将 OneToMany 添加为私有实例变量,但我不创建 #getter 方法。这样我可以在查询、设置级联删除等中使用它,但我没有提供一种方法来意外加载来自客户的数千张发票。

哦,对于那些您需要一张发票及其关联客户的查询,您应该只对发票执行 JPA 查询,然后在该发票对象上调用 #getCustomer。这将是急切地为您获取。

于 2013-06-08T17:18:51.070 回答
0

在这种情况下,您不应使用一对多关系。

一对多关系适用于“多”端的对象是“一”端对象的逻辑部分的情况(例如,从Invoice到 的关系InvoiceLine)。

Invoice在您的情况下,您需要从to的单向多对一关系Customer,以便您可以按如下方式查询它:

select i from Invoice i where ...

然后您可以使用customerfield ofInvoice来访问Customer或按其属性进行过滤。

于 2013-06-08T16:30:10.417 回答
0

如果您只需要与相关客户的一张发票,为什么不简单地基于发票创建一个查询?

select i from Invoice i where [same filtering..]
于 2013-06-08T16:15:05.707 回答