휴지 상태 조건은 FetchType을 사용하여 여러 번 하위 항목을 반환합니다.열심
나는 가지고 있다.Order
리스트가 있는 클래스OrderTransactions
1 대 다의 휴지 상태 매핑으로 매핑했습니다.
@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
모든 결과에서 인스턴스(instance)를 사용하지만, 이 시점부터는OrderTransaction
일반 SQL Join이 반환하는 것과 동일한 양의 결과를 반환해야 합니다.
그래서 실제로 그것은 여러 번 나타나야 한다.여기에서는 저자(가빈 킹) 자신이 잘 설명하고 있습니다.둘 다 명확한 결과를 얻는 이유와 방법을 설명합니다.
Also mentioned in the Hibernate [FAQ][2] :
hibernate는 컬렉션에 대해 외부 Join 페치가 활성화된 쿼리에 대해 고유한 결과를 반환하지 않습니다(distent 키워드를 사용하더라도).먼저 SQL과 OUTER JOIN이 SQL에서 어떻게 작동하는지 이해해야 합니다.SQL에서 외부 조인을 완전히 이해하고 이해하지 못하는 경우 이 FAQ 항목을 계속 읽지 말고 SQL 설명서 또는 튜토리얼을 참조하십시오.그렇지 않으면 다음 설명을 이해하지 못하고 휴지 상태 포럼에서 이 동작에 대해 불만을 제기할 수 있습니다.
동일한 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개의 요소가 포함됩니다.하이버네이트에 의해 생성되는 주문 인스턴스는 5개뿐이지만 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());
스트림은 중복을 매우 빠르게 제거합니다.엔티티 클래스에서 주석을 다음과 같이 사용합니다.
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;
데이터베이스 데이터를 필요로 하는 방법으로 세션을 사용하는 것이 앱에 있다고 생각합니다.내가 끝났을 때 폐회했다.물론 내 엔티티 클래스는 리시 가져오기 유형을 사용하도록 설정합니다.나는 리팩터에 간다.
2개의 관련 컬렉션을 가져오는 것과 같은 문제가 있습니다.사용자는 2개의 역할(세트)과 2개의 식사(목록)를 가지고 있으며 식사가 중복되어 있습니다.
@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;
...
}
DISIGNT는 도움이 되지 않습니다(DATA-JPA 쿼리):
@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);
마지막으로 두 가지 해결 방법을 찾았습니다.
- 목록을 LinkedHashSet으로 변경
- 필드 "식사"에만 EntityGraph를 사용하고 LOAD를 입력합니다. LOAD는 선언한 대로 역할을 로드합니다(N+1 문제를 방지하려면 EAGER 및 by BatchSize=200).
최종 솔루션:
@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);
다음과 같은 해킹을 사용하는 대신:
Set
List
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
sql 쿼리를 수정하지 않습니다(JPA 사양 인용)
q.select(emp).distinct(true);
쿼리(sql query)가되어 sql 에는 sql이 포함되어 있습니다.DISTINCT
안에.
외부 결합을 적용하여 중복된 결과를 가져오는 것은 좋은 행동이 아닌 것 같습니다.남은 유일한 해결책은 스트림을 사용하여 결과를 필터링하는 것입니다.필터링을 쉽게 할 수 있는 java8에 감사드립니다.
return results.stream().distinct().collect(Collectors.toList());
언급URL : https://stackoverflow.com/questions/1995080/hibernate-criteria-returns-children-multiple-times-with-fetchtype-eager
'itsource' 카테고리의 다른 글
특정 날짜 이전의 레코드 삭제 (0) | 2022.09.23 |
---|---|
v-for 루프에서 클릭한 항목에 대해서만 요소 가시성을 트리거하는 방법 (0) | 2022.09.21 |
발신인을 제외한 모든 클라이언트에 응답 보내기 (0) | 2022.09.21 |
window.location=와 window.location.replace()의 차이점은 무엇입니까? (0) | 2022.09.21 |
Python에서 메모리를 명시적으로 해방하려면 어떻게 해야 합니까? (0) | 2022.09.21 |