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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3941797/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 04:03:55  来源:igfitidea点击:

How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?

javajpaeclipselink

提问by Hanno Fietz

I have the following two annotated classes that I use to build a graph:

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

@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 */
}

Now, when I build the graph, I issue two queries to fetch all rows from either table and set up the child / parent references, for which I need the ids stored in the Edgetable.

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

Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily* loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.

因为我已经在 J​​PA 中定义了两个表之间的关系,当 JPA 提供程序延迟加载关联节点时,访问边缘对象以获取两个节点的 id 会触发每个边缘的两个 SQL 语句。由于我已经有了节点对象,并且已经从边缘表加载了 id,我想跳过这些查询,因为它们需要很长时间才能获得更大的图形。

I tried adding these lines to the Edgeclass, but then my JPA provider wants me to make one mapping read-only, and I can't seem to find a way how to do that:

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

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

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

I'm using Eclipselink and MySQL, if it matters.

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



**The default behaviour for @ManyToOneactually is eager loading, see Pascal's answer*

**@ManyToOne实际上的默认行为是预先加载,请参阅Pascal 的回答*

采纳答案by Hanno Fietz

I got three good answers that were equally helpful, and by now none percolated to the top by public vote, so I'm merging them together here for a single comprehensive answer:

我得到了三个同样有用的好答案,现在没有一个通过公众投票进入最高,所以我在这里将它们合并在一起以获得一个全面的答案:

a) Change the query

a) 更改查询

You can load the whole graph at once by changing the query, thereby giving the JPA provider a chance to realize that it already has everything in memory and doesn't need to go back to the DB:

您可以通过更改查询一次加载整个图形,从而让 JPA 提供程序有机会意识到它已经在内存中拥有所有内容并且不需要返回数据库:

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

(via axtavt)

(通过axtavt

b) Use read-only fields for the FKs

b) 对 FK 使用只读字段

Loading the FKs into their own fields, as described in the question, will also work if, as the JPA provider is demanding, the fields are declared to be readonly, which is done like this:

如问题中所述,将 FK 加载到它们自己的字段中也将起作用,如果 JPA 提供程序要求将这些字段声明为只读,这样做的方法如下:

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

(via bravocharlie)

(通过bravocharlie

c) Use property access

c) 使用属性访问

If you are using property accessinstead of field access, the JPA provider also gets a chance to realize it already has the FK and doesn't need to fetch the referenced object. In short, property access means that you put the JPA annotations on the getter, thereby "promising" the JPA provider that your getter won't go and access the rest of the object. More details in this question. This will work for Hibernate, and for Eclipselink, it will work (assumed in the original answer, experimentally confirmed by me) with weaving enabled. (via Pascal Thivent)

如果您使用property access而不是field access,JPA 提供者也有机会意识到它已经拥有 FK 并且不需要获取引用的对象。简而言之,属性访问意味着您将 JPA 注释放在 getter 上,从而“承诺”JPA 提供者您的 getter 不会去访问对象的其余部分。这个问题的更多细节。这将适用于 Hibernate,而对于 Eclipselink,它将在启用编织的情况下工作(在原始答案中假设,由我实验证实)。(通过帕斯卡·蒂文特



Additionally, as Pascal points out in his answer, @ManyToOne, contrary to my original post, is not lazy-loading, but eager-loading by default, and changing that will require weaving as well.

此外,由于出他的答案帕斯卡点@ManyToOne相反,我原来的职位,是不是延迟加载,但默认情况下急于加载,并改变将需要编织为好。

回答by bravocharlie

Have you tried

你有没有尝试过

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

回答by Pascal Thivent

How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?

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

In theory, a JPA provider should be able to not trigger a query when calling

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

someEdge.getNodeFrom().getId()

as it already has the id (as FK).

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

I'm 100% sure Hibernate can (assuming you're using property access). In the case of EclipseLink, I don't know (if it does, it will probably requires weaving).

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

Because I have defined the relation between the two tables in JPA, accessing the edge object to get the two nodes' ids triggers two SQL statements per edge, when the JPA provider lazily loads the associated nodes. Since I already have the node objects, and the ids have already been loaded from the edge table, I want to skip those queries, as they take an awfully long time for larger graphs.

因为我已经在 J​​PA 中定义了两个表之间的关系,当 JPA 提供程序延迟加载关联的节点时,访问边缘对象以获取两个节点的 id 会触发每个边缘的两个 SQL 语句。由于我已经有了节点对象,并且已经从边缘表加载了 id,我想跳过这些查询,因为它们需要很长时间才能获得更大的图形。

Note that @ManyToOneuses an EAGERstrategy by default. If you want to make it LAZY, you have to decalre it explicitly (but again, this will require weaving of your classes with EclipseLink).

请注意,默认情况下@ManyToOne使用EAGER策略。如果你想实现它LAZY,你必须显式地对其进行 decalre(但同样,这将需要使用 EclipseLink 编织你的类)。

回答by axtavt

I think you should try to optimize your query rather than change the mapping. For example, the following query fetches the whole graph at once (tested in Hibernate):

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

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

回答by Mike Argyriou

How about using getReference()?

使用 getReference() 怎么样?

For example:

例如:

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

[1] This will not trigger a SQL query to retrieve the nodeFrom

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