如何在不触及目标表的情况下从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