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


112

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. 그래서 제 경우에는 분명히 가입하라고 말했습니다.


5
자주 묻는 질문 항목에 대한 링크는 여기에 작동하고, 깨진
n0weak

답변:


99

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

5
이러한 설정이 존재하는 이유는 무엇입니까? JOIN은 거의 항상 사용해야한다고 생각합니다. 이제 모든 매핑을 최대 절전 모드 관련 주석으로 표시해야합니다.
vbezhenar

4
흥미롭지 만 슬프게도 @Fetch (FetchMode.JOIN)는 JPQL에서 Criteria에서와 같이 나에게 전혀 작동하지 않습니다 (Hibernate 4.2.15).
Aphax 2015-08-11

3
최대 절전 모드 주석은 스프링 JPA 사용하거나 나를 위해 모두에서 작동하는 것 같다하지 않습니다
TheBakker

2
@Aphax Hibernate가 JPAQL / Criteria 대 em.find ()에 대해 다른 기본 전략을 사용하기 때문일 수 있습니다. vladmihalcea.com/2013/10/17/… 및 참조 문서를 참조 하세요 .
Joshua Davis

1
@vbezhenar (그리고 얼마 후에 그의 코멘트를 읽는 다른 사람들) : JOIN 쿼리는 데이터베이스에서 데카르트 곱을 생성하므로 해당 데카르트 곱을 계산해야합니다. 페치 조인을 사용하는 경우 LAZY를 입력하더라도 즉시로드됩니다.
Walfrat

45

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

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

@Fetch(FetchMode.JOIN)

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


6
이것은 JPQL 또는 Criteria에서 Hibernate 4.2.15에서 작동하지 않습니다.
Aphax 2015-08-11

6
@Aphax JPAQL과 Criteria가 Fetch 사양을 따르지 않기 때문이라고 생각합니다. Fetch 주석은 em.find (), AFAIK에서만 작동합니다. vladmihalcea.com/2013/10/17/…을 참조하십시오 . 또한 최대 절전 모드 문서를 참조하십시오. 나는 이것이 어딘가에서 다뤄질 것이라고 확신합니다.
조슈아 데이비스

@JoshuaDavis 내가 의미하는 것은 \ @Fetch 주석이 JPQL 또는 em.find ()와 같은 쿼리에서 어떤 종류의 JOIN 최적화도 적용하지 않는다는 것입니다. 저는 Hibernate 5.2. +에서 또 다른 시도를했지만 여전히 동일합니다
Aphax

38

fetchType 속성은 기본 엔티티를 가져올 때 주석이 달린 필드를 즉시 가져올 지 여부를 제어합니다. fetch 문이 어떻게 구성되는지 반드시 지시하는 것은 아니며 실제 SQL 구현은 toplink / hibernate 등을 사용하는 공급자에 따라 다릅니다.

fetchType=EAGER이를 설정 하면 주석이 추가 된 필드가 엔터티의 다른 필드와 동시에 해당 값으로 채워집니다. 따라서 entitymanager를 열고 사람 객체를 검색 한 다음 entitymanager를 닫으면 이후에 person.address를 수행해도 지연로드 예외가 발생하지 않습니다.

설정 fetchType=LAZY하면 필드에 액세스 할 때만 채워집니다. 엔티티 관리자를 닫은 경우 person.address를 수행하면 지연로드 예외가 발생합니다. 필드를로드하려면 em.merge ()를 사용하여 엔터티를 다시 entitymangers 컨텍스트에 넣은 다음 필드 액세스를 수행 한 다음 entitymanager를 닫아야합니다.

고객 주문 컬렉션이있는 고객 클래스를 구성 할 때 지연로드를 원할 수 있습니다. 고객 목록을 얻고 싶을 때 고객의 모든 주문을 검색했다면 고객 이름과 연락처 정보 만 찾을 때 비용이 많이 드는 데이터베이스 작업이 될 수 있습니다. 나중에 db 액세스를 남겨 두는 것이 가장 좋습니다.

질문의 두 번째 부분-최적화 된 SQL을 생성하기 위해 최대 절전 모드를 얻는 방법은 무엇입니까?

Hibernate를 사용하면 가장 효율적인 쿼리를 구성하는 방법에 대한 힌트를 제공 할 수 있지만 테이블 구성에 문제가 있다고 생각합니다. 테이블에 관계가 설정되어 있습니까? Hibernate는 특히 인덱스 등이 누락 된 경우 간단한 쿼리가 조인보다 빠르다고 결정할 수 있습니다.


20

시도해보십시오 :

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

그것은 JPA2 /는 EclipseLink와 비슷한에 나를 위해 작동하지만이 기능이 존재하는 것 같다 너무 JPA1 :



2

가입하려면 여러 가지 일을 할 수 있습니다 (eclipselink 사용)

  • jpql에서는 왼쪽 조인 가져 오기를 수행 할 수 있습니다.

  • 명명 된 쿼리에서 쿼리 힌트를 지정할 수 있습니다.

  • TypedQuery에서 다음과 같이 말할 수 있습니다.

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

  • 일괄 가져 오기 힌트도 있습니다.

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

보다

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


1

Person 클래스에 키 클래스가 포함되어 있다는 점을 제외하고는 정확히이 문제가 발생했습니다. 내 자신의 솔루션은 쿼리에 조인하고 제거 하는 것이 었습니다.

@Fetch(FetchMode.JOIN)

내 삽입 된 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();
    }

}

-1

두 가지 일이 발생합니다.

첫째, 주소로 ManyToOne을 의미합니까? 즉, 여러 사람이 동일한 주소를 갖게됩니다. 그중 하나에 대해 편집 된 경우 모두에 대해 편집됩니다. 그게 당신의 의도입니까? 99 %의 시간 주소는 "비공개"입니다 (단 한 사람에게만 속한다는 의미에서).

둘째, Person 엔터티에 대해 다른 열의적인 관계가 있습니까? 내가 올바르게 기억하면 Hibernate는 엔티티에 대한 열망 관계를 하나만 처리 할 수 ​​있지만 이는 아마도 구식 정보 일 수 있습니다.

이것이 어떻게 작동해야하는지에 대한 귀하의 이해가 내가 앉아있는 곳에서 본질적으로 정확하기 때문입니다.


이것은 다 대일을 사용하는 구성된 예제입니다. 개인 주소가 가장 좋은 예가 아닐 수도 있습니다. 내 코드에 다른 eager fetch 유형이 표시되지 않습니다.
Steve Kuo

내 제안은 이것을 실행하고 실행하는 간단한 예제로 줄인 다음 게시하는 것입니다. 예상치 못한 동작을 일으키는 다른 문제가 모델에있을 수 있습니다.
cletus

1
위에 표시된대로 정확하게 코드를 실행했으며 해당 동작을 나타냅니다.
Steve Kuo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.