hibernate 자동 entity 중복제거를 피하는 방법

22·2024년 3월 28일

JPA

목록 보기
1/1

WHEN?

회사에서 중복된 엔티티들을 조회하여 각 entity 의 counting 을 하는 작업을 하려고 했습니다.
그런데 엔티티들이 다 중복이 제거돼서 리턴됐다?!

예시

User와 Record 엔티티가 있습니다. 1:N 관계입니다.
User가 Record 에 저장된 것만큼? User를 조회하고 싶습니다.

# querydsl 

    fun findUsersEntity(): List<UserEntity> {
        return from(QUserEntity.userEntity)
            .leftjoin(QRecordEntity.recordEntity)
            .on(QUserEntity.userEntity.id.eq(QRecordEntity.recordEntity.fkId))
            .fetch()
    }
# sql 

select
        ue1_0.id,
        ue1_0.name
    from
        user ue1_0
    left join
        record re1_0
            on ue1_0.id=re1_0.fk_id;
  • 같은 쿼리로 보여 응답값도 같은 것으로 예측되지만, 다른 응답값을 가져온다.

api 응답

sql 리턴

기대했던 응답값은 sql 값과 같습니다. 같은 쿼리이지만 왜 api 응답과 쿼리의 결과는 다른걸까요?



WHY?

하이버네이트 5

  • 일대다 조인하여 부모만 select 하면, 자식 수 만큼 부모 엔티티가 늘어나는 이슈가 있었습니다. 그걸 해결하기 위해 JPQL 에 distinct 를 붙였습니다.
  • 하지만 SQL distinct 는 그냥 select 보다 연산이 많아 성능적으로 불리합니다 (실행계획 참조)
  • org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH 옵션을 false 로 주면 SQL distinct 쿼리가 발생하지 않고 하이버네이트가 어플리케이션에서 중복제거를 해줍니다.

하이버네이트 6

  • SQL distinct 쿼리를 만들지 않고 어플리케이션에서 중복제거를 하도록 바뀌었습니다. (하이버네이트 6.0 마이그레이션 가이드에서는 다르게 설명되어있어서 확인 필요)
  • HINT_PASS_DISTINCT_THROUGH 옵션이 아예 없어졌습니다.



HOW?


org.hibernate.sql.results.spi.ListResultsConsumer<R>

중복제거가 되는 과정

  • 1: 리턴타입이 Entity 이고 UniqueSemantic.ALLOW 가 True면 EntityResults를 사용합니다.
  • 2: addUnique() 를 하기 때문에, 여기서 중복제거가 됩니다.

방법1 : UniqueSemantic.ALLOW 설정을 바꿀 수 없을까 ?


org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan<R>

  • 처음 쿼리를 요청할 때 각 쿼리에 맞는 UniqueSemantic 값이 세팅됩니다.
  • 제 요구사항에는 중복된 결과가 응답돼서 유니크하지 않기 때문에 ALLOW 가 세팅됩니다.
  • 만약 findByName() 같은 쿼리를 사용한다면 NONE 이 세팅됩니다.

이 값은 SQM 이 판단하기 때문에 제가 옵션을 세팅할 수 없었습니다.

SQM 이란? Semantic Query Model 로 하이버네이트 6부터 도입됐습니다.
It is the new entity query parser that addresses both JPQL and Criteria API.



해결

방법2 : result 가 entity 가 아닌 경우

  • 1: 1에서 False 이기 때문에 Results를 사용합니다.
  • 3: add() 를 합니다. 중복제거가 되지 않기 때문에 중복된 데이터를 리턴합니다.

Reference

https://in.relation.to/2016/08/04/introducing-distinct-pass-through-query-hint/
https://docs.jboss.org/hibernate/orm/6.0/migration-guide/migration-guide.html#query-sqm-distinct

0개의 댓글