developer tip

최대 절전 모드 기준은 FetchType.EAGER를 사용하여 자식을 여러 번 반환합니다.

optionbox 2020. 7. 28. 08:26
반응형

최대 절전 모드 기준은 FetchType.EAGER를 사용하여 자식을 여러 번 반환합니다.


Order목록 이있는 클래스가 있고 OrderTransactions일대 다 최대 절전 모드 매핑으로 매핑했습니다.

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

Order들 또한 필드가 orderStatus다음과 같은 기준으로 필터링을 위해 사용된다 :

public List<Order> getOrderForProduct(OrderFilter orderFilter) {
    Criteria criteria = getHibernateSession()
            .createCriteria(Order.class)
            .add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));
    return criteria.list();
}

이 작동하고 결과는 예상대로입니다.

지금 여기 내 질문은 : 왜, 나는이 명시 적으로 유형을 가져 설정할 때 EAGER의 수행 Order결과 목록에서의 여러 번 표시?

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

새 설정과 동일한 결과를 얻으려면 기준 코드를 어떻게 변경해야합니까?


구성을 올바르게 이해하면 실제로 예상되는 동작입니다.

Order결과에서 동일한 인스턴스를 얻었지만 OrderTransaction이제는와 조인을 수행하므로 일반 SQL 조인이 반환하는 것과 동일한 양의 결과를 반환해야합니다.

따라서 실제로 여러 번 배가 되어야합니다 . 이것은 저자 (Gavin King) 자신에 의해 잘 설명되어 있습니다 : 그것은 왜, 왜 뚜렷한 결과를 얻는 지 설명합니다.


최대 절전 모드 FAQ에 언급되어 있습니다 .

Hibernate는 컬렉션에 대해 외부 조인 페치를 사용하는 쿼리에 대해 고유 한 결과를 반환하지 않습니다 (differential 키워드를 사용하더라도)? 먼저 SQL과 OUTER JOIN이 SQL에서 작동하는 방식을 이해해야합니다. SQL에서 외부 조인을 완전히 이해하고 이해하지 못하는 경우이 FAQ 항목을 계속 읽지 말고 SQL 매뉴얼 또는 자습서를 참조하십시오. 그렇지 않으면 다음 설명을 이해하지 못하며 Hibernate 포럼에서이 동작에 대해 불평 할 것입니다.

동일한 Order 오브젝트의 중복 참조를 리턴 할 수있는 일반적인 예제 :

List result = session.createCriteria(Order.class)
                    .setFetchMode("lineItems", FetchMode.JOIN)
                    .list();

<class name="Order">
    ...
    <set name="lineItems" fetch="join">

List result = session.createCriteria(Order.class)
                       .list();
List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();

이 모든 예제는 동일한 SQL 문을 생성합니다.

SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID

왜 중복이 있는지 알고 싶습니까? SQL 결과 집합을 살펴보십시오. Hibernate는 외부 조인 결과의 왼쪽에서 이러한 복제본을 숨기지 않지만 구동 테이블의 모든 복제본을 반환합니다. 데이터베이스에 5 개의 광고 주문이 있고 각 주문에 3 개의 광고 항목이있는 경우 결과 집합은 15 행입니다. 이러한 쿼리의 Java 결과 목록에는 Order 유형의 15 개 요소가 있습니다. Hibernate는 5 개의 Order 인스턴스 만 생성하지만, SQL 결과 세트의 복제본은이 5 개의 인스턴스에 대한 중복 참조로 유지됩니다. 이 마지막 문장을 이해하지 못하면 Java에서 Java 힙의 인스턴스와 해당 인스턴스에 대한 참조의 차이점을 읽어야합니다.

(왼쪽 외부 조인이 필요한 이유 광고 항목이없는 추가 주문이있는 경우 결과 항목은 16 개의 행으로, 오른쪽을 채우는 NULL로, 광고 항목 데이터가 다른 주문에 대한 것입니다. 광고 항목이없는 경우 그렇지 않은 경우 HQL에서 내부 조인 페치를 사용하십시오.

최대 절전 모드는 기본적으로 이러한 중복 참조를 필터링하지 않습니다. 어떤 사람들은 (당신이 아닌) 실제로 이것을 원합니다. 어떻게 걸러 낼 수 있습니까?

이처럼 :

Collection result = new LinkedHashSet( session.create*(...).list() );

Eran이 언급 한 것 외에도 원하는 동작을 얻는 또 다른 방법은 결과 변환기를 설정하는 것입니다.

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

시험

@Fetch (FetchMode.SELECT) 

예를 들어

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch (FetchMode.SELECT)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;

}


List와 ArrayList를 사용하지 말고 Set과 HashSet을 사용하십시오.

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public Set<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

Java 8 및 Streams를 사용하여 유틸리티 메소드 에이 반환 통계를 추가합니다.

return results.stream().distinct().collect(Collectors.toList());

스트림은 중복을 매우 빠르게 제거합니다. 다음과 같이 Entity 클래스에서 주석을 사용합니다.

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;

I think is bether in my app to use session in method where I need data from data base. Closse session when I done. Ofcourse set my Entity class to use leasy fetch type. I go to refactor.


I have the same problem to fetch 2 associated collections: user has 2 roles (Set) and 2 meals (List) and meals are duplicated.

@Table(name = "users")
public class User extends AbstractNamedEntity {

   @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
   @Column(name = "role")
   @ElementCollection(fetch = FetchType.EAGER)
   @BatchSize(size = 200)
   private Set<Role> roles;

   @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
   @OrderBy("dateTime DESC")
   protected List<Meal> meals;
   ...
}

DISTINCT doesn't help (DATA-JPA query):

@EntityGraph(attributePaths={"meals", "roles"})
@QueryHints({@QueryHint(name= org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")}) // remove unnecessary distinct from select
@Query("SELECT DISTINCT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);

At last I've found 2 solutions:

  1. Change List to LinkedHashSet
  2. Use EntityGraph with only field "meal" and type LOAD, which load roles as they declared (EAGER and by BatchSize=200 to prevent N+1 problem):

Final solution:

@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);

UPDATE: unless javadoc for org.springframework.data.jpa.repository.EntityGraph.EntityGraphType#FETCH

attributes that are specified by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are treated as FetchType.LAZY

with this type roles also fetched.


It doesn't sounds a great behavior applying an outer join and bring duplicated results. The only solution left is to filter our result using streams. Thanks java8 giving easier way to filter.

return results.stream().distinct().collect(Collectors.toList());

참고URL : https://stackoverflow.com/questions/1995080/hibernate-criteria-returns-children-multiple-times-with-fetchtype-eager

반응형