JPA eager fetch没有加入

时间:2022-09-16 06:44:55

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的获取策略控制到底是什么?我看不出急切和懒惰之间有什么区别。在这两种情况下,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 each person:

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的查询日志。我的理解是,将地址的fetch类型设置为eager将导致JPA/Hibernate使用join自动查询。但是,无论获取类型是什么,它仍然为关系生成不同的查询。

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.

我是不是漏掉了什么?现在我必须手工编写每个查询的代码,以便它保留多对一关系。我正在使用Hibernate的JPA实现和MySQL。

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

编辑:似乎(请参阅这里和这里的Hibernate FAQ) FetchType不会影响JPA查询。在我的例子中,我明确地告诉它加入。

8 个解决方案

#1


84  

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

So SELECT and JOIN are two extremes and SUBSELECT falls in between. One can choose suitable strategy based on her/his domain model.

所以SELECT和JOIN是两个极端,SUBSELECT介于两者之间。可以根据其领域模型选择合适的策略。

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

默认情况下,JPA/EclipseLink和Hibernate都使用SELECT。可以通过以下方式覆盖:

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

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

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

Corresponding annotations in EclipseLink are:

EclipseLink中的对应注解是:

@JoinFetch
@BatchFetch

#2


42  

"mxc" is right. fetchType just specifies when the 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.

你的领域。这是一个特定于hibernate的注释。

#3


36  

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=EAGER This 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。地址不会导致抛出延迟加载异常。

If you set fetchType=LAZY the 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()的entitymangers上下文中,然后进行字段访问,然后关闭entitymanager。

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.

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

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

对于问题的第二部分——如何让hibernate生成优化的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可能认为一个简单的查询比一个连接要快,特别是在缺少索引等情况下。

#4


17  

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中:

#5


7  

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/ example /JPA/QueryOptimization一文。

There is a chapter about "Joined Reading".

有一章是关于“加入阅读”的。

#6


2  

to join you can do multiple things (using eclipselink)

要加入,您可以做很多事情(使用eclipselink)

  • in jpql you can do left join fetch

    在jpql中,可以执行左连接获取

  • in named query you can specify query hint

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

  • in TypedQuery you can say something like

    在TypedQuery中,可以这样说。

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

    query.setHint(“eclipselink。join-fetch”、“e.projects.milestones”);

  • there is also batch fetch hint

    还有批量获取提示

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

    query.setHint(“eclipselink。批处理”、“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

#7


1  

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();
    }

}

#8


-1  

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).

首先,你确定你指的是地址吗?这意味着很多人会有相同的地址。如果它是为其中一个而编辑的,它将为所有的人而编辑。那是你的意图吗?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.

我这么说是因为你们对这个应该怎么做的理解,从我现在的位置来看,基本上是正确的。

#1


84  

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

So SELECT and JOIN are two extremes and SUBSELECT falls in between. One can choose suitable strategy based on her/his domain model.

所以SELECT和JOIN是两个极端,SUBSELECT介于两者之间。可以根据其领域模型选择合适的策略。

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

默认情况下,JPA/EclipseLink和Hibernate都使用SELECT。可以通过以下方式覆盖:

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

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

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

Corresponding annotations in EclipseLink are:

EclipseLink中的对应注解是:

@JoinFetch
@BatchFetch

#2


42  

"mxc" is right. fetchType just specifies when the 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.

你的领域。这是一个特定于hibernate的注释。

#3


36  

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=EAGER This 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。地址不会导致抛出延迟加载异常。

If you set fetchType=LAZY the 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()的entitymangers上下文中,然后进行字段访问,然后关闭entitymanager。

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.

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

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

对于问题的第二部分——如何让hibernate生成优化的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可能认为一个简单的查询比一个连接要快,特别是在缺少索引等情况下。

#4


17  

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中:

#5


7  

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/ example /JPA/QueryOptimization一文。

There is a chapter about "Joined Reading".

有一章是关于“加入阅读”的。

#6


2  

to join you can do multiple things (using eclipselink)

要加入,您可以做很多事情(使用eclipselink)

  • in jpql you can do left join fetch

    在jpql中,可以执行左连接获取

  • in named query you can specify query hint

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

  • in TypedQuery you can say something like

    在TypedQuery中,可以这样说。

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

    query.setHint(“eclipselink。join-fetch”、“e.projects.milestones”);

  • there is also batch fetch hint

    还有批量获取提示

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

    query.setHint(“eclipselink。批处理”、“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

#7


1  

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();
    }

}

#8


-1  

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).

首先,你确定你指的是地址吗?这意味着很多人会有相同的地址。如果它是为其中一个而编辑的,它将为所有的人而编辑。那是你的意图吗?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.

我这么说是因为你们对这个应该怎么做的理解,从我现在的位置来看,基本上是正确的。