Programming

JPA eager fetch가 참여하지 않습니다.

procodes 2020. 8. 14. 21:09
반응형

JPA eager fetch가 참여하지 않습니다.


JPA의 가져 오기 전략은 정확히 무엇을 제어합니까? 나는 eager와 lazy의 차이를 감지 할 수 없습니다. 두 경우 모두 JPA / Hibernate는 다 대일 관계를 자동으로 결합하지 않습니다.

예 : 개인은 단일 주소를 가지고 있습니다. 주소는 많은 사람에게 속할 수 있습니다. 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;
}

JPA 쿼리를 사용하는 경우 :

select p from Person p where ...

JPA / Hibernate는 Person 테이블에서 선택할 하나의 SQL 쿼리를 생성 한 다음 사람에 대한 고유 한 주소 쿼리를 생성 합니다 .

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

이것은 큰 결과 집합에 매우 좋지 않습니다. 1000 명의 사람이있는 경우 1001 개의 쿼리를 생성합니다 (Person에서 1 개, Address에서 1000 개). MySQL의 쿼리 로그를보고 있기 때문에 이것을 알고 있습니다. 주소의 가져 오기 유형을 eager로 설정하면 JPA / Hibernate가 자동으로 조인으로 쿼리한다는 것을 이해했습니다. 그러나 페치 유형에 관계없이 관계에 대해 고유 한 쿼리를 생성합니다.

내가 명시 적으로 조인하도록 지시 할 때만 실제로 조인됩니다.

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

여기에 뭔가 빠졌나요? 이제 다 대일 관계를 조인하도록 모든 쿼리를 직접 코딩해야합니다. MySQL과 함께 Hibernate의 JPA 구현을 사용하고 있습니다.

편집 : JPA 쿼리에 영향 주지 않는 것으로 나타납니다 ( 여기여기 Hibernate FAQ 참조 ) FetchType. 그래서 제 경우에는 분명히 가입하라고 말했습니다.


JPA는 가져 오기 전략을 선택하기위한 매핑 주석에 대한 사양을 제공하지 않습니다. 일반적으로 관련 항목은 아래에 제공된 방법 중 하나로 가져올 수 있습니다.

  • SELECT => 루트 엔터티에 대한 하나의 쿼리 + 관련된 매핑 된 엔터티에 대한 하나의 쿼리 / 각 루트 엔터티의 컬렉션 = (n + 1) 쿼리
  • SUBSELECT => 루트 엔터티에 대한 쿼리 1 개 + 매핑 된 관련 엔터티에 대한 두 번째 쿼리 / 첫 번째 쿼리에서 검색된 모든 루트 엔터티 컬렉션 = 2 개 쿼리
  • JOIN => 루트 엔터티와 매핑 된 모든 엔터티 / 컬렉션을 가져 오는 하나의 쿼리 = 1 개의 쿼리

그래서 SELECTJOIN두 극단이 있고 SUBSELECT사이에 빠진다. 자신의 도메인 모델에 따라 적절한 전략을 선택할 수 있습니다.

기본적으로 SELECTJPA / EclipseLink와 Hibernate에서 모두 사용됩니다. 다음을 사용하여 재정의 할 수 있습니다.

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

Hibernate에서. 예를 들어 배치 크기를 사용하여 조정할 수있는 SELECT모드를 명시 적으로 사용하여 설정할 수도 있습니다 .@Fetch(FetchMode.SELECT)@BatchSize(size=10)

EclipseLink의 해당 주석은 다음과 같습니다.

@JoinFetch
@BatchFetch

"mxc"가 맞습니다. 관계가 해결되어야하는 시기를fetchType 지정합니다 .

외부 조인을 사용하여 즉시로드를 최적화하려면 다음을 추가해야합니다.

@Fetch(FetchMode.JOIN)

당신의 분야에. 이것은 최대 절전 모드 특정 주석입니다.


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.

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.

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.

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?

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.


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:


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.

There is a chapter about "Joined Reading".


to join you can do multiple things (using 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");

see

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


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

@Fetch(FetchMode.JOIN)

My embedded id class:

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

}

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

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.

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

참고URL : https://stackoverflow.com/questions/463349/jpa-eager-fetch-does-not-join

반응형