Java JPA 急切获取不加入

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/463349/
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-08-11 14:56:54  来源:igfitidea点击:

JPA eager fetch does not join

javahibernatejpajoin

提问by Steve Kuo

What exactly does JPA's fetch strategy control? I can't detect any difference between eager and lazy. In both cases JPA/Hibernate does not automatically join many-to-one relationships.

JPA 的 fetch 策略究竟控制什么?我无法检测到渴望和懒惰之间的任何区别。在这两种情况下,JPA/Hibernate 都不会自动加入多对一关系。

Example: Person has a single address. An address can belong to many people. The JPA annotated entity classes look like:

示例:Person 有一个地址。一个地址可以属于很多人。JPA 注释的实体类如下所示:

@Entity
public class Person {
    @Id
    public Integer id;

    public String name;

    @ManyToOne(fetch=FetchType.LAZY or EAGER)
    public Address address;
}

@Entity
public class Address {
    @Id
    public Integer id;

    public String name;
}

If I use the JPA query:

如果我使用 JPA 查询:

select p from Person p where ...

JPA/Hibernate generates one SQL query to select from Person table, and then a distinct address query for eachperson:

JPA/Hibernate 生成一个 SQL 查询以从 Person 表中进行选择,然后为每个人生成一个不同的地址查询:

select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3

This is very bad for large result sets. If there are 1000 people it generates 1001 queries (1 from Person and 1000 distinct from Address). I know this because I'm looking at MySQL's query log. It was my understanding that setting address's fetch type to eager will cause JPA/Hibernate to automatically query with a join. However, regardless of the fetch type, it still generates distinct queries for relationships.

这对于大型结果集来说非常糟糕。如果有 1000 个人,它会生成 1001 个查询(1 个来自 Person,1000 个与 Address 不同)。我知道这一点是因为我正在查看 MySQL 的查询日志。我的理解是,将地址的提取类型设置为eager会导致 JPA/Hibernate 使用连接自动查询。但是,无论提取类型如何,它仍然会为关系生成不同的查询。

Only when I explicitly tell it to join does it actually join:

只有当我明确告诉它加入时,它才会真正加入:

select p, a from Person p left join p.address a where ...

Am I missing something here? I now have to hand code every query so that it left joins the many-to-one relationships. I'm using Hibernate's JPA implementation with MySQL.

我在这里错过了什么吗?我现在必须手动编码每个查询,以便它离开加入多对一关系。我在 MySQL 中使用 Hibernate 的 JPA 实现。

Edit:It appears (see Hibernate FAQ hereand here) that FetchTypedoes not impact JPA queries. So in my case I have explicitly tell it to join.

编辑:它似乎(请参阅此处此处的Hibernate 常见问题解答)FetchType不会影响 JPA 查询。所以就我而言,我已经明确告诉它加入。

回答by cletus

Two things occur to me.

我想到了两件事。

First, are you sure you mean ManyToOne for address? That means multiple people will have the same address. If it's edited for one of them, it'll be edited for all of them. Is that your intent? 99% of the time addresses are "private" (in the sense that they belong to only one person).

首先,你确定你的地址是 ManyToOne 吗?这意味着多人将拥有相同的地址。如果为其中之一编辑了它,则会为所有这些编辑。这是你的意图吗?99% 的时间地址是“私人的”(从某种意义上说,它们只属于一个人)。

Secondly, do you have any other eager relationships on the Person entity? If I recall correctly, Hibernate can only handle one eager relationship on an entity but that is possibly outdated information.

其次,您是否对 Person 实体有任何其他渴望的关系?如果我没记错的话,Hibernate 只能处理一个实体上的一种急切关系,但这可能是过时的信息。

I say that because your understanding of how this should work is essentially correct from where I'm sitting.

我这么说是因为你对这应该如何工作的理解从我坐的地方来看基本上是正确的。

回答by mxc

The fetchType attribute controls whether the annotated field is fetched immediately when the primary entity is fetched. It does not necessarily dictate how the fetch statement is constructed, the actual sql implementation depends on the provider you are using toplink/hibernate etc.

fetchType 属性控制在获取主要实体时是否立即获取带注释的字段。它不一定决定 fetch 语句的构造方式,实际的 sql 实现取决于您使用的提供程序 toplink/hibernate 等。

If you set fetchType=EAGERThis means that the annotated field is populated with its values at the same time as the other fields in the entity. So if you open an entitymanager retrieve your person objects and then close the entitymanager, subsequently doing a person.address will not result in a lazy load exception being thrown.

如果您设置fetchType=EAGER这意味着注释字段将与实体中的其他字段同时填充其值。因此,如果您打开一个 entitymanager 检索您的 person 对象,然后关闭该 entitymanager,则随后执行 person.address 不会导致抛出延迟加载异常。

If you set fetchType=LAZYthe field is only populated when it is accessed. If you have closed the entitymanager by then a lazy load exception will be thrown if you do a person.address. To load the field you need to put the entity back into an entitymangers context with em.merge(), then do the field access and then close the entitymanager.

如果您设置fetchType=LAZY该字段仅在访问时填充。如果您已经关闭了 entitymanager,那么在您执行 person.address 时将抛出延迟加载异常。要加载字段,您需要使用 em.merge() 将实体放回实体管理器上下文中,然后进行字段访问,然后关闭实体管理器。

You might want lazy loading when constructing a customer class with a collection for customer orders. If you retrieved every order for a customer when you wanted to get a customer list this may be a expensive database operation when you only looking for customer name and contact details. Best to leave the db access till later.

在构建包含客户订单集合的客户类时,您可能需要延迟加载。如果您在想要获取客户列表时检索了客户的每个订单,当您只查找客户姓名和联系方式时,这可能是一项昂贵的数据库操作。最好将数据库访问权留到以后。

For the second part of the question - how to get hibernate to generate optimised SQL?

对于问题的第二部分 - 如何让休眠生成优化的 SQL?

Hibernate should allow you to provide hints as to how to construct the most efficient query but I suspect there is something wrong with your table construction. Is the relationship established in the tables? Hibernate may have decided that a simple query will be quicker than a join especially if indexes etc are missing.

Hibernate 应该允许您提供有关如何构建最有效查询的提示,但我怀疑您的表构建有问题。表中是否建立了关系?Hibernate 可能已经决定一个简单的查询将比连接更快,尤其是在缺少索引等的情况下。

回答by rudolfson

"mxc" is right. fetchTypejust specifies whenthe relation should be resolved.

“mxc”是对的。fetchType只是指定何时应该解决关系。

To optimize eager loading by using an outer join you have to add

要使用外连接优化预先加载,您必须添加

@Fetch(FetchMode.JOIN)

to your field. This is a hibernate specific annotation.

到你的领域。这是一个休眠特定的注释。

回答by u?6??n? ???u?p

If you use EclipseLink instead of Hibernate you can optimize your queries by "query hints". See this article from the Eclipse Wiki: EclipseLink/Examples/JPA/QueryOptimization.

如果您使用 EclipseLink 而不是 Hibernate,您可以通过“查询提示”优化您的查询。请参阅 Eclipse Wiki 中的这篇文章:EclipseLink/Examples/JPA/QueryOptimization

There is a chapter about "Joined Reading".

有一章是关于“联合阅读”的。

回答by sinuhepop

Try with:

尝试:

select p from Person p left join FETCH p.address a where...

It works for me in a similar with JPA2/EclipseLink, but it seems this feature is present in JPA1 too:

这对我的作品与JPA2 / EclipseLink的相似,但似乎这个功能存在于JPA1太

回答by Gunslinger

I had exactly this problem with the exception that the Person class had a embedded key class. My own solution was to join them in the query AND remove

我遇到了这个问题,除了 Person 类有一个嵌入的键类。我自己的解决方案是将它们加入查询并删除

@Fetch(FetchMode.JOIN)

@Fetch(FetchMode.JOIN)

My embedded id class:

我的嵌入式 ID 类:

@Embeddable
public class MessageRecipientId implements Serializable {

    @ManyToOne(targetEntity = Message.class, fetch = FetchType.LAZY)
    @JoinColumn(name="messageId")
    private Message message;
    private String governmentId;

    public MessageRecipientId() {
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    public String getGovernmentId() {
        return governmentId;
    }

    public void setGovernmentId(String governmentId) {
        this.governmentId = governmentId;
    }

    public MessageRecipientId(Message message, GovernmentId governmentId) {
        this.message = message;
        this.governmentId = governmentId.getValue();
    }

}

回答by Yadu

JPA doesn't provide any specification on mapping annotations to select fetch strategy. In general, related entities can be fetched in any one of the ways given below

JPA 没有提供任何关于映射注释以选择获取策略的规范。一般来说,可以通过下面给出的任何一种方式获取相关实体

  • SELECT => one query for root entities + one query for related mapped entity/collection of each root entity = (n+1) queries
  • SUBSELECT => one query for root entities + second query for related mapped entity/collection of all root entities retrieved in first query = 2 queries
  • JOIN => one query to fetch both root entities and all of their mapped entity/collection = 1 query
  • SELECT => 一个根实体查询 + 一个相关映射实体/每个根实体集合的查询 = (n+1) 个查询
  • SUBSELECT => 一个根实体查询 + 第二个相关映射实体/第一个查询中检索到的所有根实体集合的第二个查询 = 2 个查询
  • JOIN => 一个查询来获取两个根实体及其所有映射实体/集合 = 1 个查询

So SELECTand JOINare two extremes and SUBSELECTfalls in between. One can choose suitable strategy based on her/his domain model.

所以SELECTJOIN是两个极端SUBSELECT,介于两者之间。可以根据她/他的领域模型选择合适的策略。

By default SELECTis used by both JPA/EclipseLink and Hibernate. This can be overridden by using:

默认情况下SELECT由 JPA/EclipseLink 和 Hibernate 使用。这可以通过使用:

@Fetch(FetchMode.JOIN) 
@Fetch(FetchMode.SUBSELECT)

in Hibernate. It also allows to set SELECTmode explicitly using @Fetch(FetchMode.SELECT)which can be tuned by using batch size e.g. @BatchSize(size=10).

在休眠中。它还允许SELECT显式地设置模式@Fetch(FetchMode.SELECT),可以使用批处理大小进行调整,例如@BatchSize(size=10)

Corresponding annotations in EclipseLink are:

EclipseLink 中对应的注解有:

@JoinFetch
@BatchFetch

回答by Kalpesh Soni

to join you can do multiple things (using eclipselink)

加入你可以做多件事(使用eclipselink)

  • in jpql you can do left join fetch

  • in named query you can specify query hint

  • in TypedQuery you can say something like

    query.setHint("eclipselink.join-fetch", "e.projects.milestones");

  • there is also batch fetch hint

    query.setHint("eclipselink.batch", "e.address");

  • 在 jpql 中,您可以进行左连接提取

  • 在命名查询中,您可以指定查询提示

  • 在 TypedQuery 你可以这样说

    query.setHint("eclipselink.join-fetch", "e.projects.milestones");

  • 还有批量提取提示

    query.setHint("eclipselink.batch", "e.address");

see

http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html

http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html