[java] 최대 절전 모드에서 MultipleBagFetchException이 발생 함-여러 백을 동시에 가져올 수 없음

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을 가져올 수 없다 .

최악의 “솔루션”

이제 많은 답변, 블로그 게시물, 비디오 또는 기타 SetList 컬렉션에 대신 를 찾을 수 있습니다.

그것은 끔찍한 충고입니다. 하지마!

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_THROUGHJPA 쿼리 힌트를로 설정 한 이유 false입니다.

DISTINCT는 JPQL에서 두 가지 의미를 지니고 있으며 여기서 getResultListSQL 측이 아닌 Java 측에서 리턴 된 Java 오브젝트 참조를 중복 제거해야합니다 . 확인 이 문서 자세한 내용을.

를 사용하여 최대 하나의 컬렉션을 가져 오는 한 JOIN FETCH괜찮습니다.

여러 쿼리를 사용하면 다른 컬렉션이 있지만 첫 번째 컬렉션은 보조 쿼리를 사용하여 가져 오기 때문에 Cartesian Product를 피할 수 있습니다.

할 수있는 일이 더 있습니다

FetchType.EAGER매핑 시간 @OneToMany또는 @ManyToMany연결 시 전략을 사용하는 경우 쉽게MultipleBagFetchException .

당신은에서 전환 것이 더 낫다 FetchType.EAGER에 대한 Fetchype.LAZY열망 페치가 있기 때문에 중요한 애플리케이션의 성능 문제가 발생할 수 있습니다 끔찍한 생각 .

결론

FetchType.EAGER와 전환하지 않습니다 ListSet이렇게하면 최대 절전 모드를 만들해서이를 숨길 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