Jpa 에서는 보통 다른 객체와의 연관관계를 맺어줄 때 직접참조(Direct Reference: @OneToMany, @ManyToOne 등
) 를 하고, Lazy fetch 로 해당 객체에 접근하는 방식을 주로 사용한다. 이럴 경우 Lazy fetch 라면, Proxy 객체를 만들어 가지고 있다가 실제 객체에 접근할 때 쿼리를 날려 fetch 해온다. 이는 N+1 Problem 을 야기할 수 있는데, 이 때문에 종종 id 로만 조회해와야하는 경우들이 생긴다.
ManyToOne
의 경우 id 로 조회하려고 하면 결국 해당 연관된 객체에 한번 접근해야하기 때문에, Many 측에서 접근할 때마다 다시 쿼리를 날리게 되고 똑같은 N+1 문제가 발생하게 된다.
이때 아래와 같은 방법을 이용하면 쉽게 하나의 컬럼을 여러 field/property 로 매핑할 수 있다.
@Entity
class Team (
@Id
val id: UUID = UUID.randomUUID(),
val name: String = "",
@OneToMany(fetch = FetchType.LAZY, mappedBy = "team")
val members: MutableList<Member> = mutableListOf()
)
@Entity
class Member (
@Id
val id: UUID = UUID.randomUUID(),
val name: String = "",
team: Team? = null,
// ⭐⭐⭐
@Column(name = "team_id", insertable = false, updatable = false)
val teamId: UUID? = null,
) {
@ManyToOne(fetch = LAZY, cascade = [CascadeType.PERSIST])
@JoinColumn(name = "team_id")
var team: Team? = null
set(value) {
field = value
team?.let { it.members += this }
}
init {
this.team = team
}
}
@SpringBootTest
internal class Test {
@Autowired
private lateinit var teamRepo: TeamRepo
@Autowired
private lateinit var memberRepo: MemberRepo
@Test
fun `fetch teamId`() {
val team = Team(name = "ONiON").also(teamRepo::save)
val memberId1 = Member(name = "Vita", team = team).also(memberRepo::save).id
val memberId2 = Member(name = "Max", team = team).also(memberRepo::save).id
val member1 = memberRepo.findByIdOrNull(memberId1)
val member2 = memberRepo.findByIdOrNull(memberId2)
assertNotNull(member1)
assertNotNull(member2)
member1!!.let {
assertNotNull(it.team)
assertEquals(team.id, it.teamId)
}
member2!!.let {
assertNotNull(it.team)
assertEquals(team.id, it.teamId)
}
}
}
interface TeamRepo: JpaRepository<Team, UUID>
interface MemberRepo: JpaRepository<Member, UUID>
2. Member.kt 의 teamId 프로퍼티에서 볼 수 있다시피, @Column 안에 insertable = false, updatable = false
옵션을 주면 쉽게 여러 프로퍼티에 매핑이 가능하다.
하나라도 누락될 경우
Caused by: org.hibernate.MappingException: Column 'team_id' is duplicated in mapping for entity '....Member' (use '@Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)
at app//org.hibernate.mapping.PersistentClass.checkColumnDuplication(PersistentClass.java:1009)
예외가 발생한다.