Hibernate는 SessionFactory 생성 동안이 예외를 던진다 :
org.hibernate.loader.MultipleBagFetchException : 여러 백을 동시에 가져올 수 없음
이것은 내 테스트 사례입니다.
Parent.java
@Entity
public Parent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
// @IndexColumn(name="INDEX_COL") if I had this the problem solve but I retrieve more children than I have, one child is null.
private List<Child> children;
}
Child.java
@Entity
public Child {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Parent parent;
}
이 문제는 어떻습니까? 어떡해?
편집하다
좋아, 내가 가진 문제는 또 다른 “부모”개체가 부모 내부에 있다는 것입니다. 실제 행동은 다음과 같습니다.
Parent.java
@Entity
public Parent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private AnotherParent anotherParent;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<Child> children;
}
AnotherParent.java
@Entity
public AnotherParent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
private List<AnotherChild> anotherChildren;
}
최대 절전 모드는로 두 컬렉션을 좋아하지 FetchType.EAGER
않지만 이것은 버그 인 것 같습니다. 특별한 일을하지 않습니다 …
제거 FetchType.EAGER
에서 Parent
또는 AnotherParent
문제가 해결하지만, 실제 솔루션을 사용하는 것입니다, 그래서 나는 그것을 필요로 @LazyCollection(LazyCollectionOption.FALSE)
대신 FetchType
(덕분에 Bozho 솔루션에 대한).
답변
최신 버전의 최대 절전 모드 (JPA 2.0 지원)가이를 처리해야한다고 생각합니다. 그러나 그렇지 않으면 컬렉션 필드에 주석을 달아서 해결할 수 있습니다.
@LazyCollection(LazyCollectionOption.FALSE)
에서 fetchType
속성 을 제거해야 합니다@*ToMany
주석 .
그러나 대부분의 경우 a Set<Child>
가보다 적합 List<Child>
하므로 실제로List
에 대한 이동을 –Set
그러나 세트를 사용하면 Vlad Mihalcea가 그의 답변에서 설명한 것처럼 밑받침이되는 직교 곱을 제거 하지 않습니다 !
답변
간단히 List
유형 에서 유형으로 변경하십시오 Set
.
그러나 Vlad Mihalcea가 그의 답변에서 설명한 것처럼 밑받침이되는 직교 곱을 제거 하지는 않을 것입니다 !
답변
코드에 Hibernate 고유의 @Fetch 주석을 추가하십시오.
@OneToMany(mappedBy="parent", fetch=FetchType.EAGER)
@Fetch(value = FetchMode.SUBSELECT)
private List<Child> childs;
이것은 최대 절전 모드 버그 HHH-1718 과 관련된 문제를 해결해야합니다.
답변
이 질문은 StackOverflow 또는 Hibernate 포럼에서 반복되는 주제이므로 답변을 기사 로 전환하기로 결정했습니다 .
우리는 다음과 같은 엔티티를 가지고 있다고 생각합니다.
그리고 당신은 부모를 가져오고 싶어 Post
모두와 함께 실체를 comments
하고tags
컬렉션 .
둘 이상을 사용하는 경우 JOIN FETCH
지시문을 :
List<Post> posts = entityManager
.createQuery(
"select p " +
"from Post p " +
"left join fetch p.comments " +
"left join fetch p.tags " +
"where p.id between :minId and :maxId", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.getResultList();
최대 절전 모드는 악명 높은 것을 던질 것입니다.
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags [
com.vladmihalcea.book.hpjp.hibernate.fetching.Post.comments,
com.vladmihalcea.book.hpjp.hibernate.fetching.Post.tags
]
Hibernate는 Cartesian product를 생성하기 때문에 하나 이상의 bag을 가져올 수 없다 .
최악의 “솔루션”
이제 많은 답변, 블로그 게시물, 비디오 또는 기타 Set
List
컬렉션에 대신 를 찾을 수 있습니다.
그것은 끔찍한 충고입니다. 하지마!
Sets
대신에 사용Lists
하면MultipleBagFetchException
사라,하지만 당신은 당신이 “수정”을 적용 긴 후 성능 문제를 찾을 수 있습니다으로 직교 제품은 여전히 실제로 더 악화되는,있을 것입니다.
적절한 해결책
다음과 같은 트릭을 수행 할 수 있습니다.
List<Post> posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.id between :minId and :maxId ", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.tags t " +
"where p in :posts ", Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
첫 번째 JPQL 조회
distinct
에서 SQL 문으로 이동하지 마십시오. 이것이PASS_DISTINCT_THROUGH
JPA 쿼리 힌트를로 설정 한 이유false
입니다.DISTINCT는 JPQL에서 두 가지 의미를 지니고 있으며 여기서
getResultList
SQL 측이 아닌 Java 측에서 리턴 된 Java 오브젝트 참조를 중복 제거해야합니다 . 확인 이 문서 자세한 내용을.
를 사용하여 최대 하나의 컬렉션을 가져 오는 한 JOIN FETCH
괜찮습니다.
여러 쿼리를 사용하면 다른 컬렉션이 있지만 첫 번째 컬렉션은 보조 쿼리를 사용하여 가져 오기 때문에 Cartesian Product를 피할 수 있습니다.
할 수있는 일이 더 있습니다
FetchType.EAGER
매핑 시간 @OneToMany
또는 @ManyToMany
연결 시 전략을 사용하는 경우 쉽게MultipleBagFetchException
.
당신은에서 전환 것이 더 낫다 FetchType.EAGER
에 대한 Fetchype.LAZY
열망 페치가 있기 때문에 중요한 애플리케이션의 성능 문제가 발생할 수 있습니다 끔찍한 생각 .
결론
피 FetchType.EAGER
와 전환하지 않습니다 List
에 Set
이렇게하면 최대 절전 모드를 만들해서이를 숨길 MultipleBagFetchException
카펫 아래에. 한 번에 하나의 컬렉션 만 가져 오면 괜찮을 것입니다.
컬렉션을 초기화 할 때와 같은 수의 쿼리로 수행하는 한 괜찮습니다. 루프에서 컬렉션을 초기화하지 마십시오. N + 1 쿼리 문제가 발생 하기 때문에 성능에 좋지 않습니다.
답변
이 게시물과 다른 게시물에 설명 된 모든 단일 옵션으로 시도한 후에 수정 사항은 다음과 같습니다.
모든 XToMany 장소에서 @ XXXToMany(mappedBy="parent", fetch=FetchType.EAGER)
및 중간 이후
@Fetch(value = FetchMode.SUBSELECT)
이것은 나를 위해 일했다
답변
이를 고치려면 단순히 중첩 된 객체 Set
대신 List
사용하십시오.
@OneToMany
Set<Your_object> objectList;
그리고 사용하는 것을 잊지 마세요 fetch=FetchType.EAGER
작동합니다.
하나 더 개념이 있습니다 CollectionId
목록만을 고수하고 싶다면 최대 절전 모드에 .
그러나 Vlad Mihalcea가 그의 답변에서 설명한 것처럼 밑받침이되는 직교 곱을 제거 하지는 않을 것입니다 !
답변
이러한 종류의 객체 매핑에서 Hibernate의 동작에 대한 좋은 블로그 게시물을 찾았습니다. http://blog.eyallupu.com/2010/06/hibernate-exception-simultaneously.html