如何在不触及目标表的情况下从JPA ManyToOne映射中检索外键?

我有以下两个用于构建图形的带注释的类:

@Entity @Table(name = "Edge") public class Edge { /* some code omitted for brevity */ @ManyToOne @JoinColumn(name = "ixNodeFrom", nullable = false) private Node _nodFrom; @ManyToOne @JoinColumn(name = "ixNodeTo", nullable = false) private Node _nodTo; /* some code omitted for brevity */ } @Entity @Table(name = "Node") public class Node { /* some code omitted for brevity */ @OneToMany(mappedBy = "_nodTo") private Set _rgInbound; @OneToMany(mappedBy = "_nodFrom") private Set _rgOutbound; /* some code omitted for brevity */ } 

现在,当我构建图形时,我发出两个查询来从任一个表中获取所有行并设置子/父引用,为此我需要存储在Edge表中的id。

因为我已经在JPA中定义了两个表之间的关系,所以当JPA提供者懒洋洋地加载关联节点时,访问边缘对象以获取两个节点的ID会触发每个边缘两个SQL语句。 由于我已经有了节点对象,并且已经从边缘表加载了id,因此我想跳过这些查询,因为它们需要花费很长时间来处理较大的图形。

我尝试将这些行添加到Edge类中,但是我的JPA提供程序希望我将一个映射设置为只读,而我似乎无法找到如何执行此操作的方法:

 @Column(name = "ixNodeTo") private long _ixNodeTo; @Column(name = "ixNodeFrom") private long _ixNodeFrom; 

我正在使用Eclipselink和MySQL,如果重要的话。


** @ManyToOne的默认行为实际上是急切加载,请参阅Pascal的答案 *

我得到了三个同样有帮助的好答案,现在没有人通过公众投票渗透到最高层,所以我将它们合并在一起以获得一个全面的答案:

a)更改查询

您可以通过更改查询立即加载整个图形,从而使JPA提供程序有机会意识到它已经拥有内存中的所有内容,而不需要返回到DB:

 List nodes = em.createQuery( "SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound") .getResultList(); 

(通过axtavt )

b)使用FK的只读字段

如果像JPA提供者要求的那样,将字段声明为只读字段,那么将FK添加到他们自己的字段中(如问题中所述)也将起作用,如下所示:

 @Column(name = "ixNodeTo", insertable = false, updatable = false) 

(通过bravocharlie )

c)使用财产访问权限

如果您使用属性访问而不是字段访问 ,JPA提供程序也有机会意识到它已经拥有FK并且不需要获取引用的对象。 简而言之,属性访问意味着您将JPA注释放在getter上,从而“承诺”您的getter不会去的JPA提供者并访问该对象的其余部分。 这个问题的更多细节。 这适用于Hibernate,对于Eclipselink,它将起作用(在原始答案中假定,由我实验确认)并启用编织。 (通过Pascal Thivent )


另外,正如Pascal在他的回答中指出的那样 ,与我原来的post相反, @ManyToOne不是延迟加载,而是默认加载,并且改变它也需要编织。

你有没有尝试过

 @Column(name = "ixNodeTo", insertable = false, updatable = false) 

如何在不触及目标表的情况下从JPA ManyToOne映射中检索外键?

理论上,JPA提供程序应该能够在调用时不触发查询

 someEdge.getNodeFrom().getId() 

因为它已经有了id(作为FK)。

我100%肯定Hibernate可以(假设你正在使用属性访问 )。 在EclipseLink的情况下,我不知道(如果是,它可能需要编织)。

因为我已经在JPA中定义了两个表之间的关系,所以当JPA提供程序懒洋洋地加载关联节点时,访问edge对象以获取两个节点的id会触发每个边缘两个SQL语句。 由于我已经有了节点对象,并且已经从边缘表加载了id,因此我想跳过这些查询,因为它们需要花费很长时间来处理较大的图形。

请注意,默认情况下, @ManyToOne使用EAGER策略。 如果你想让它成为LAZY ,你必须明确地对它进行十分转换(但同样,这需要用EclipseLink编写你的类)。

我认为您应该尝试优化查询而不是更改映射。 例如,以下查询一次获取整个图形(在Hibernate中测试):

 List nodes = em.createQuery( "SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound") .getResultList(); 

使用getReference()怎么样?

例如:

 Node fkNode = em.getReference(edge.getNodeFrom()); // [1] fkNode.getId() 

[1]这不会触发SQL查询来检索nodeFrom