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)
}
문제 원인
해당쿼리를 직접 데이터베이스에 실행시켜보면 원인은 간단히 이해가 된다.
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에서 중복된 데이터는 제거하는 기능을 제공해준다.
결과
'게으른개발자 > 공부' 카테고리의 다른 글
OSI 7Layer와 L1, L2, L3, L4, L7 스위치 (0) | 2022.10.30 |
---|---|
REST vs. RESTful (0) | 2022.09.13 |
SSH 터널링을 통한 데이터베이스 백업 방법 (0) | 2022.07.16 |
JPA 를 사용하여 JSON 문자열을 객체에 매핑하기 (0) | 2021.12.14 |
JPA 외래키로 복합키 만들기 (0) | 2021.11.22 |