[java] 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)

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


답변

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

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

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

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

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

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


답변

시도해보십시오 :

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

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


답변

Hibernate 대신 EclipseLink를 사용하는 경우 “쿼리 힌트”로 쿼리를 최적화 할 수 있습니다. Eclipse Wiki에서이 기사를 참조하십시오 : EclipseLink / Examples / JPA / QueryOptimization .

“Joined Reading”에 대한 장이 있습니다.


답변

가입하려면 여러 가지 일을 할 수 있습니다 (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


답변

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

}