본문 바로가기

게으른개발자/공부

JPA OneToMany 중복조회

JPA 에서 OneToMany를 사용할경우 예상치 못하게 동일한 데이터가 중복 조회되는 경우가 있다.

 

 

배경

아래와 같이 두개의 Entity가 있는 상황있다고 가정하자.

@Entity
class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false)
    var id: Long? = null

    @Column(columnDefinition = "varchar(30)")
    var name: String? = null

    @OneToMany(mappedBy = "team")
    var members = mutableListOf<Member>()
}
@Entity
class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    var id: Long? = null

    @Column(columnDefinition = "varchar(30)")
    var name: String? = null

    @ManyToOne
    @JoinColumn(name = "team_id")
    var team: Team? = null
}

 

두 엔티티는 (Team: Member -> 1:N) 연관관계가 맺어져있고, 아래와 같은 데이터가 들어가있다.

문제 상황

아래와 같이, findByIdWithMembers라는 메소드를 통해  Team의 PK 값인 id로  Team 데이터를 조회 하였다. id 는 유일값이기 때문에 Team 객체는 1개만 조회되었을것이라 예상 가능하다.

 

interface TeamRepository: JpaRepository<Team, Long> {
    @Query("select t from Team t  join fetch t.members m where t.id = :teamId ")
    fun findByIdWithMembers(teamId: Long) : List<Team>
}

 

그러나, 아래 테스트 코드를 실행시켜보면 의외의 결과에 당황하게 된다.

    @Test
    fun selectTest() {
        val teams = teamRepository.findByIdWithMembers(1L)
        Assertions.assertThat(teams.size).isEqualTo(1)
    }

결과는 1개가 아닌 2개의 Team이 조회된다.

 

 

문제 원인

해당쿼리를 직접 데이터베이스에 실행시켜보면 원인은 간단히 이해가 된다.

join으로 인해 2개의 결과가 조회

JPA를 통해 실행될 쿼리를 직접 실행하면 join결과를 통해  id 가 1인 Team  객체가 2개가 조회된다.

 

JPA입장에서는 위의 결과를 토대로 Entity에 매핑을하게되고, 그에 따라 우리가 원하는 것과는 달리 id가 1인 Team이 2개가 조회가 되어진다.

 

해결 방안

JPA에서 제공해주는 distinct 를 활용해서 해결이가능하다.

 

interface TeamRepository: JpaRepository<Team, Long> {
//   AS-IS  @Query("select t from Team t  join fetch t.members m where t.id = :teamId ")
    @Query("select DISTINCT t from Team t  join fetch t.members m where t.id = :teamId ")
    fun findByIdWithMembers(teamId: Long) : List<Team>
}

 

위와 같이 distinct 명령어를 사용하면, 쿼리자체로는 중복된 데이터가 조회지만 

JPA에서 중복된 데이터는 제거하는 기능을 제공해준다.

 

결과

테스트 성공