我有一个与发票实体有OneToMany关系的客户实体。
在普通的旧版本中,我可以“从客户那里选择customer_name,customer_age,[其他一些字段],发票在哪里...... [在这里放一些过滤]”,这样我就能得到一条带有我需要的字段的记录。 / p>
在JPA中,我使用“从客户c中选择c加入c.invoiceCollection,其中...... [与上面相同的过滤]”
这样可行,但我通过所有关联的发票获得了Customer实体。 这是无稽之谈,因为我从数据库中提取了大量(发票)数据,这是我不需要的。我只需要一个发票的客户数据,如where子句中所指定的那样。
更糟糕的是,我必须循环遍历Customer.invoiceCollection才能找到所需的特定发票。这花费了我更多的时间加上它将我的“where”子句暴露给中间层。
问题:是否存在JPA select语法,该语法从一对多关系中提取一条记录,如where子句中所定义的那样?
到目前为止尝试的事情: a)延迟加载。这不起作用,每当我尝试访问Customer.invoiceCollection时都会抛出异常。 即使它有效,我也会得到一个包含大约1000个条目的集合,这是我不需要的。
b)将我的jpa语句改为“select c,i from Customer c join c.invoiceCollection i where ...”。这会返回一个对象数组,我必须手动映射到Customer / Invoice实体。 它有效,但它使ORM哲学过时了。如果我在代码中手动执行从关系数据库记录/字段到Java对象的所有映射,为什么我需要JPA?
答案 0 :(得分:1)
这是关于JPA最令人愤怒的事情之一。例如,如果您希望客户级联删除发票,则需要OneToMany端。在大多数情况下,您希望在删除客户时告知发票自行删除,以便客户不一定需要了解发票。
我的建议是,让OneToMany保留在那里,但让Lazy Loading正常工作。在您的代码中,不要直接访问“Customer#getInvoices”(除非您确实需要所有这些)。
这将允许您对加入发票而不加载发票的客户进行查询。
我猜你所遇到的例外与交易边界有关,可以轻松修复。
对于很多这些关系,我经常将OneToMany添加为私有实例变量,但我不创建#getter方法。这样我就可以在查询,设置级联删除等中使用它,但我没有提供一种方法来意外地从客户加载数千张发票。
哦,对于那些您需要与其关联客户签订一张发票的查询,您应该只在发票上执行JPA查询,然后在该发票对象上调用#getCustomer。这将是你急切的想法。
答案 1 :(得分:0)
如果您只需要与相关客户签发一张发票,为什么不根据发票创建查询?
select i from Invoice i where [same filtering..]
答案 2 :(得分:0)
在这种情况下,您不应该使用一对多关系。
一对多关系适用于“多”侧的对象是“一”侧对象的逻辑部分(例如从Invoice
到InvoiceLine
的关系)的情况。
在您的情况下,您需要从Invoice
到Customer
的单向多对一关系,以便您可以按如下方式查询:
select i from Invoice i where ...
然后,您可以使用customer
的{{1}}字段访问Invoice
或按其属性进行过滤。