我有以下两个用于构建图形的带注释的类:
@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<Edge> _rgInbound;
@OneToMany(mappedBy = "_nodFrom")
private Set<Edge> _rgOutbound;
/* some code omitted for brevity */
}
现在,当我构建图形时,我发出两个查询来从任一表中获取所有行并设置子/父引用,为此我需要存储在Edge
表中的id。
因为我已经在JPA中定义了两个表之间的关系,所以当JPA提供程序 lazily *加载关联时,访问edge对象以获取两个节点的id会触发每个边缘两个SQL语句节点。由于我已经有了节点对象,并且已经从边缘表加载了id,因此我想跳过这些查询,因为它们需要花费很长时间来处理较大的图形。
我尝试将这些行添加到Edge
类,但是我的JPA提供程序要求我将一个映射设置为只读,而我似乎无法找到如何执行此操作的方法:
@Column(name = "ixNodeTo")
private long _ixNodeTo;
@Column(name = "ixNodeFrom")
private long _ixNodeFrom;
如果重要的话,我正在使用Eclipselink和MySQL。
** @ManyToOne
的默认行为实际上是急切加载,请参阅Pascal's answer *
答案 0 :(得分:14)
我得到了三个同样有帮助的好答案,现在没有人通过公众投票渗透到最高层,所以我将它们合并在一起以获得一个全面的答案:
您可以通过更改查询来立即加载整个图形,从而使JPA提供程序有机会意识到它已经拥有内存中的所有内容而无需返回到数据库:
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
(通过axtavt)
如问题中所述,将FK加载到他们自己的字段中,如果JPA提供者要求将字段声明为只读字段,那么它也将起作用,如下所示:
@Column(name = "ixNodeTo", insertable = false, updatable = false)
(通过bravocharlie)
如果您使用属性访问而不是字段访问,JPA提供程序也有机会意识到它已经拥有FK并且不需要获取引用对象。简而言之,属性访问意味着您将JPA注释放在getter上,从而“承诺”JPA提供者,您的getter将不会访问该对象的其余部分。 this question中的更多详细信息。这适用于Hibernate,对于Eclipselink,它将起作用(在原始答案中假定,由我实验确认)并启用编织。 (通过Pascal Thivent)
此外,正如Pascal points out in his answer,@ManyToOne
,与我原来的帖子相反,不是延迟加载,而是默认加载,并且更改它也需要编织。
答案 1 :(得分:11)
你试过吗
@Column(name = "ixNodeTo", insertable = false, updatable = false)
答案 2 :(得分:3)
如何在不敲击目标表的情况下从JPA ManyToOne映射中检索外键?
理论上,JPA提供者在调用
时应该不能触发查询someEdge.getNodeFrom().getId()
因为它已经有了id(作为FK)。
我100%肯定Hibernate可以(假设您正在使用property access)。在EclipseLink的情况下,我不知道(如果是的话,它可能需要编织)。
因为我已经在JPA中定义了两个表之间的关系,所以当JPA提供程序懒洋洋地加载关联节点时,访问edge对象以获取两个节点的id会触发每个边缘两个SQL语句。由于我已经有了节点对象,并且已经从边缘表加载了id,因此我想跳过这些查询,因为它们需要花费很长时间来处理较大的图形。
请注意,@ManyToOne
默认使用EAGER
策略。如果你想要它LAZY
,你必须明确地去掉它(但同样,这需要用EclipseLink编织你的类)。
答案 3 :(得分:2)
我认为您应该尝试优化查询而不是更改映射。例如,以下查询一次获取整个图形(在Hibernate中测试):
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
答案 4 :(得分:0)
如何使用getReference()?
例如:
Node fkNode = em.getReference(edge.getNodeFrom()); // [1]
fkNode.getId()
[1]这不会触发SQL查询来检索nodeFrom