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 개의 쿼리
그래서 SELECT
와 JOIN
두 극단이 있고 SUBSELECT
사이에 빠진다. 자신의 도메인 모델에 따라 적절한 전략을 선택할 수 있습니다.
기본적으로 SELECT
JPA / 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();
}
}