DevLog πΆ
[JPA] CascadeType.REMOVE vs orphanRemoval=true μ°¨μ΄μ μμ보기 - 2νΈ λ³Έλ¬Έ
[JPA] CascadeType.REMOVE vs orphanRemoval=true μ°¨μ΄μ μμ보기 - 2νΈ
dolmeng2 2023. 6. 30. 11:14π± λ€μ΄κ°κΈ° μ
μ§λ ν¬μ€ν μμλ CascadeType.REMOVEμ λν΄μ μ€μ μ μΌλ‘ μμλ΄€μλλ°, μ΄λ²μλ orphanRemoval=true μ΅μ μ λν΄μ ν λ² μμ보μ. μν°ν° μΈν μ μ§λ λ²κ³Ό κ±°μ λμΌνκΈ° λλ¬Έμ λ³νκ° μκΈ΄ λΆλΆμ λν΄μλ§ λ°λ‘ μ§λλ‘ νκ² λ€.
π± μν°ν° μμ νκΈ°
μ΄λ²μλ CascadeType.REMOVE λμ μ orphanRemoval=trueλ₯Ό μ μ©νμ.
@Entity
class Concert(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,
@Column(nullable = false)
val name: String,
@Column(nullable = false)
val ticketLimit: Int,
concertTickets: MutableList<ConcertTicket> = Collections.emptyList()
) {
@OneToMany(
fetch = FetchType.LAZY,
mappedBy = "concert",
cascade = [CascadeType.PERSIST],
orphanRemoval = true // here!
)
val concertTickets: MutableList<ConcertTicket> = concertTickets.toMutableList()
}
π± orphanRemoval = true
π¬ λΆλͺ¨ μν°ν° μ κ±°νκΈ° - μ½μνΈ μν°ν° μ κ±°
@Test
@DisplayName("orphanRemoval=true ν
μ€νΈ - λΆλͺ¨ μν°ν° μ κ±°")
fun orphanRemoval_true_test_λΆλͺ¨_μν°ν°_μ κ±°() {
// given
val μ½μνΈ = Concert(name = "μΈκΈ° λ§μ μ½μνΈ", ticketLimit = 10)
val μ½μνΈ_ν°μΌ1 = ConcertTicket(userId = 1L, concert = μ½μνΈ)
val μ½μνΈ_ν°μΌ2 = ConcertTicket(userId = 2L, concert = μ½μνΈ)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ1)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ2)
concertRepository.save(μ½μνΈ)
val μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(2)
// when
concertRepository.delete(μ½μνΈ)
// then
val μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€).hasSize(0)
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(0)
}
νλμ μ½μνΈ μν°ν°μ λν΄ 2κ°μ μ½μνΈ ν°μΌ μν°ν°κ° μ‘΄μ¬νλ ννμ΄λ€.
μ΄λ, μ½μνΈ μν°ν°λ₯Ό μ κ±°νκ² λλ©΄ CascadeType.REMOVEμ λ§μ°¬κ°μ§λ‘ λΆλͺ¨μ μ°κ΄λ μμ μν°ν°λ ν¨κ» μ κ±°λμ΄ μ‘°νλ μ½μνΈ ν°μΌ μν°ν°μ κ°μκ° 0κ°μΈ κ²μ νμΈν μ μλ€.
μ€μ λ‘ λ°μν 쿼리λ₯Ό 보λλΌλ μ½μνΈ ν°μΌ μν°ν° 2κ°μ λν delete 쿼리μ μ½μνΈ μν°ν°μ λν 1κ°μ delete μΏΌλ¦¬κ° λ°μνμ¬, μ΄ 3κ°μ μΏΌλ¦¬κ° λκ° κ²μ νμΈν μ μλ€.
π¬ λΆλͺ¨ μν°ν°μ μμ μν°ν° μ°κ΄κ΄κ³ λκΈ°
@Test
@DisplayName("orphanRemoval=true ν
μ€νΈ - λΆλͺ¨ μν°ν°μμ μμ μν°ν° μ κ±°")
fun orphanRemoval_true_test_λΆλͺ¨_μν°ν°μμ_μμ_μν°ν°_μ κ±°() {
// given
val μ½μνΈ = Concert(name = "μΈκΈ° λ§μ μ½μνΈ", ticketLimit = 10)
val μ½μνΈ_ν°μΌ1 = ConcertTicket(userId = 1L, concert = μ½μνΈ)
val μ½μνΈ_ν°μΌ2 = ConcertTicket(userId = 2L, concert = μ½μνΈ)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ1)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ2)
concertRepository.save(μ½μνΈ)
val μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(2)
// when
μ½μνΈ.concertTickets.remove(μ½μνΈ_ν°μΌ1)
μ½μνΈ.concertTickets.remove(μ½μνΈ_ν°μΌ2)
// then
val μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€).hasSize(1) // here!
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(0)
}
μ΄λ²μλ κ³ μ κ°μ²΄λ₯Ό λ§λ κ²°κ³Όμ΄λ€.
νμ§λ§, CascadeType.REMOVEμ λ€λ₯΄κ² λ¨μν μ½μνΈ μν°ν°μ μ½μνΈ ν°μΌ μν°ν° μ¬μ΄μ κ΄κ³λ₯Ό λμ΄μ£Όλ μ½μνΈ ν°μΌ μν°ν°κ° μ€μ DBμμ μ κ±°λ κ²μ νμΈν μ μλ€. μ¦, λΆλͺ¨ μν°ν°μ μμ μν°ν°μ κ΄κ³κ° λμ΄μ§κ² λλ©΄ κ³ μ(orphan)κ° λκ³ , κ³ μ κ°μ²΄μ λν΄ delete 쿼리(remove)κ° λ°μνλ κ²μ΄λ€.
μ€μ λ‘ λ°μν 쿼리λ₯Ό νμΈνλ©΄ μ½μνΈ ν°μΌ μν°ν° 2κ°μ λν delete μΏΌλ¦¬κ° λ°μν κ²μ λ³Ό μ μλ€.
π± CascadeType.REMOVE + orphanRemoval = true ν¨κ» μ¬μ©νκΈ°
@Entity
class Concert(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,
@Column(nullable = false)
val name: String,
@Column(nullable = false)
val ticketLimit: Int,
concertTickets: MutableList<ConcertTicket> = Collections.emptyList()
) {
@OneToMany(
fetch = FetchType.LAZY,
mappedBy = "concert",
cascade = [CascadeType.PERSIST, CascadeType.REMOVE],
orphanRemoval = true
)
val concertTickets: MutableList<ConcertTicket> = concertTickets.toMutableList()
}
π¬ λΆλͺ¨ μν°ν° μ κ±°νκΈ° - μ½μνΈ μν°ν° μ κ±°
@Test
@DisplayName("CascadeType.REMOVE + orphanRemoval=true ν
μ€νΈ - λΆλͺ¨ μν°ν° μ κ±°")
fun cascadeType_remove_orphanRemoval_true_test_λΆλͺ¨_μν°ν°_μ κ±°() {
// given
val μ½μνΈ = Concert(name = "μΈκΈ° λ§μ μ½μνΈ", ticketLimit = 10)
val μ½μνΈ_ν°μΌ1 = ConcertTicket(userId = 1L, concert = μ½μνΈ)
val μ½μνΈ_ν°μΌ2 = ConcertTicket(userId = 2L, concert = μ½μνΈ)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ1)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ2)
concertRepository.save(μ½μνΈ)
val μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(2)
// when
concertRepository.delete(μ½μνΈ)
// then
val μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€).hasSize(0)
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(0)
}
CascadeType.REMOVEλ orphanRemoval = true μ΅μ λͺ¨λ λΆλͺ¨ μν°ν°λ₯Ό μ κ±°νλ©΄ μ°κ΄λ μμ μν°ν°λ ν¨κ» μ κ±°λκΈ° λλ¬Έμ λΉμ°νκ² λ μ΅μ μ ν¨κ» μ¬μ©νλ©΄ λκ°μ΄ μ κ±°λλ κ²μ λ³Ό μ μλ€.
π¬ λΆλͺ¨ μν°ν°μ μμ μν°ν° μ°κ΄κ΄κ³ λκΈ°
@Test
@DisplayName("CascadeType.REMOVE + orphanRemoval=true - λΆλͺ¨ μν°ν°μμ μμ μν°ν° μ κ±°")
fun cascadeType_remove_orphanRemoval_true_test_λΆλͺ¨_μν°ν°μμ_μμ_μν°ν°_μ κ±°() {
// given
val μ½μνΈ = Concert(name = "μΈκΈ° λ§μ μ½μνΈ", ticketLimit = 10)
val μ½μνΈ_ν°μΌ1 = ConcertTicket(userId = 1L, concert = μ½μνΈ)
val μ½μνΈ_ν°μΌ2 = ConcertTicket(userId = 2L, concert = μ½μνΈ)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ1)
μ½μνΈ.addTicket(μ½μνΈ_ν°μΌ2)
concertRepository.save(μ½μνΈ)
val μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(2)
// when
μ½μνΈ.concertTickets.remove(μ½μνΈ_ν°μΌ1)
μ½μνΈ.concertTickets.remove(μ½μνΈ_ν°μΌ2)
// then
val μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(0)
}
κ°μ μ΄μ λ‘ λΆλͺ¨ μν°ν°μ μμ μν°ν°μ μ°κ΄κ΄κ³λ₯Ό λλλΌλ orphanRemoval = true μ΅μ μ μν΄μ μ κ±°λλ κ²μ νμΈν μ μλ€.
π± orphanRemoval = trueλ μΈμ μ¬μ©μ μ§μνλ κ² μ’μκΉ?
@Entity
class Concert(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,
@Column(nullable = false)
val name: String,
@Column(nullable = false)
val ticketLimit: Int,
concertTickets: MutableList<ConcertTicket> = Collections.emptyList()
) {
@OneToMany(
fetch = FetchType.LAZY,
mappedBy = "concert"
)
val concertTickets: MutableList<ConcertTicket> = concertTickets.toMutableList()
}
@Entity
class ConcertTicket(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,
@Column(nullable = false)
val userId: Long,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "concert_id",
foreignKey = ForeignKey(name = "fk_ticket_concert_id")
)
var concert: Concert
)
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,
val name: String,
concertTickets: MutableList<ConcertTicket> = Collections.emptyList()
) {
@OneToMany(
fetch = FetchType.LAZY,
orphanRemoval = true
)
val concertTickets: MutableList<ConcertTicket> = concertTickets.toMutableList()
}
ν μ€νΈλ₯Ό μν΄μ μ¬μ©μλ₯Ό λνλ΄λ 'User' μν°ν°λ₯Ό λ§λ€κ³ , μ¬μ©μ μν°ν°κ° μ½μνΈ ν°μΌ μν°ν°λ₯Ό μμμΌλ‘ κ°μ§κ³ μλ μνλ‘ λ§λ€μλ€. μ¦, 'μ½μνΈ ν°μΌ μν°ν°'μ λν΄μλ 'μ½μνΈ'μ 'μ¬μ©μ'λΌλ λ κ°μ λΆλͺ¨ μν°ν°κ° μ‘΄μ¬νλ ννμΈ κ²μ΄λ€.
μ°Έκ³ λ‘, μ½μνΈ ν°μΌ μν°ν°μ λν΄μλ μ¬μ©μ μν°ν°μ λν μ°κ΄κ΄κ³λ₯Ό μ€μ νμ§ μμλ€. (λ¨λ°©ν₯) λν, κΈ°μ‘΄ μ½μνΈ μν°ν°μ CascadeType.PERSIST μ΅μ μ μ κ±°νκ³ μ¬μ©μ μν°ν°κ° κ°μ§ μ½μνΈ ν°μΌ μν°ν°μ λν΄μ orphanRemoval=trueλ₯Ό μΆκ°νμλ€.
@Test
@DisplayName("orphanRemoval=true - λ€μ€ λΆλͺ¨λ₯Ό κ°μ§κ³ μλ κ²½μ°, νλμ λΆλͺ¨ μμ ")
fun orphanRemoval_true_multiple_parents_λΆλͺ¨_μν°ν°_μ κ±°() {
// given
val μ½μνΈ = Concert(name = "μΈκΈ° λ§μ μ½μνΈ", ticketLimit = 10)
val μ½μνΈ_ν°μΌ1 = ConcertTicket(userId = 1L, concert = μ½μνΈ)
val μ½μνΈ_ν°μΌ2 = ConcertTicket(userId = 2L, concert = μ½μνΈ)
concertRepository.save(μ½μνΈ)
concertTicketRepository.saveAll(listOf(μ½μνΈ_ν°μΌ1, μ½μνΈ_ν°μΌ2))
val μ¬μ©μ = User(name = "μ Έλ")
userRepository.save(μ¬μ©μ)
// when
userRepository.delete(μ¬μ©μ)
// then
val μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
val λͺ¨λ _μ¬μ©μλ€ = userRepository.findAll()
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(2)
assertThat(λͺ¨λ _μ¬μ©μλ€).hasSize(0)
}
μ°μ , PERSIST μμ΄ μ¬λ¬ λΆλͺ¨κ° μμ λ νλμ λΆλͺ¨μ λν΄μ μ κ±°λ₯Ό νκ² λλ©΄ λ¨μν μ¬μ©μμ λν delete μΏΌλ¦¬λ§ λ°μνλ€.
@Entity
class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0L,
val name: String,
concertTickets: List<ConcertTicket> = Collections.emptyList()
) {
@OneToMany(
fetch = FetchType.LAZY,
cascade = [CascadeType.PERSIST],
orphanRemoval = true
)
val concertTickets: MutableList<ConcertTicket> = concertTickets.toMutableList()
}
νμ§λ§, μ¬μ©μμ μ½μνΈ μν°ν°μ λν΄μ cascade μ΅μ μ ν΅ν΄ PERSISTλ₯Ό μ€λ€λ©΄ κ²°κ³Όκ° λ¬λΌμ§κ² λλ€.
π¬ λΆλͺ¨ μν°ν° μ κ±°νκΈ° - μ¬μ©μ μν°ν° μ κ±° (λ€μ€ λΆλͺ¨)
@Test
@DisplayName("orphanRemoval=true - λ€μ€ λΆλͺ¨λ₯Ό κ°μ§κ³ μλ κ²½μ°, νλμ λΆλͺ¨ μμ ")
fun orphanRemoval_true_multiple_parents_λΆλͺ¨_μν°ν°_μ κ±°() {
// given
val μ½μνΈ = Concert(name = "μΈκΈ° λ§μ μ½μνΈ", ticketLimit = 10)
val μ½μνΈ_ν°μΌ1 = ConcertTicket(userId = 1L, concert = μ½μνΈ)
val μ½μνΈ_ν°μΌ2 = ConcertTicket(userId = 2L, concert = μ½μνΈ)
concertRepository.save(μ½μνΈ)
val μ¬μ©μ = User(name = "μ Έλ", concertTickets = mutableListOf(μ½μνΈ_ν°μΌ1, μ½μνΈ_ν°μΌ2))
userRepository.save(μ¬μ©μ)
// when
userRepository.delete(μ¬μ©μ)
// then
val μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
val λͺ¨λ _μ¬μ©μλ€ = userRepository.findAll()
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(0)
assertThat(λͺ¨λ _μ¬μ©μλ€).hasSize(0)
}
νμ§λ§, μ¬μ©μλ₯Ό μ μ₯ν λ μ½μνΈ ν°μΌμ ν¨κ» μ μ₯νκ³ (PERSISTμ μν΄μ μμν -> insert) μ¬μ©μ μν°ν°λ₯Ό μ κ±°νκ² λλ©΄ μμνλ μ½μνΈ ν°μΌ μν°ν°λ ν¨κ» μ κ±°λλ€. μ¦, βοΈ κ°μ₯ ν° ν¬μΈνΈλ μμνλ₯Ό ν΅ν΄μ λΆλͺ¨μ μμμ μλͺ μ£ΌκΈ°λ₯Ό λμΌνκ² λ§λλ κ²μ΄λ€.
λ§μ½ μ¬μ©μμ λν μ λ³΄λ§ μ κ±°νκ³ μΆκ³ μ½μνΈ ν°μΌμ λν΄μλ μ κ±°νκ³ μΆμ§ μμ μν©μ΄ λ°μνλ€λ©΄ (λ¬Όλ‘ , μ΄λ° μν©μ κ±°μ μμ κ²μ΄λ€. λλΆλΆ μμμ μλͺ μ£ΌκΈ°λ λΆλͺ¨μκ² μ’ μλλ κ²μ΄ μΌλ°μ μ΄λκΉ) orphanRemoval = trueμ μ¬μ©μ μ κ³ λ―Όν΄λ΄μΌ νλ€.
π¬ λΆλͺ¨ μν°ν°μ μμ μν°ν° μ°κ΄κ΄κ³ λκΈ°
@Test
@DisplayName("orphanRemoval=true - λ€μ€ λΆλͺ¨λ₯Ό κ°μ§κ³ μλ κ²½μ°, νλμ λΆλͺ¨μμ μμ μν°ν° μ κ±°")
fun orphanRemoval_true_multiple_parents_λΆλͺ¨μμ_μμ_μν°ν°_μ κ±°() {
// given
val μ½μνΈ = Concert(name = "μΈκΈ° λ§μ μ½μνΈ", ticketLimit = 10)
concertRepository.save(μ½μνΈ)
val μ½μνΈ_ν°μΌ1 = ConcertTicket(userId = 1L, concert = μ½μνΈ)
val μ½μνΈ_ν°μΌ2 = ConcertTicket(userId = 2L, concert = μ½μνΈ)
val μ¬μ©μ = User(name = "μ Έλ", concertTickets = mutableListOf(μ½μνΈ_ν°μΌ1, μ½μνΈ_ν°μΌ2))
userRepository.save(μ¬μ©μ)
// when
μ¬μ©μ.concertTickets.remove(μ½μνΈ_ν°μΌ1)
μ¬μ©μ.concertTickets.remove(μ½μνΈ_ν°μΌ2)
// then
val μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€ = concertRepository.findAll()
val μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€ = concertTicketRepository.findAll()
val λͺ¨λ _μ¬μ©μλ€ = userRepository.findAll()
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈλ€).hasSize(1)
assertThat(μμ _μ΄ν_μ μ₯λ_μ½μνΈ_ν°μΌλ€).hasSize(0)
assertThat(λͺ¨λ _μ¬μ©μλ€[0].concertTickets).hasSize(0)
}
μ΄λ²μλ μμμ± μ μ΄ μ΅μ μ μ€μ ν λΆλͺ¨ μν°ν° (μ¬μ©μ)μμ μμ μν°ν° (μ½μνΈ ν°μΌ)μ λν μ°κ΄κ΄κ³λ₯Ό λμ΄λ³΄μ. μ½μνΈ ν°μΌ μν°ν°λ DB μμΌλ‘ μ½μνΈμ μ°κ΄κ΄κ³κ° μμμλ μμμ± μ μ΄ μ΅μ μ΄ μ€μ λμ΄ μλ μ¬μ©μμ μ½μνΈ ν°μΌ μ¬μ΄μ μ°κ΄κ΄κ³κ° λμ΄μ§λ μ κ±°λλ κ²μ λ³Ό μ μλ€.
π± κ²°λ‘
- μμμ± μ μ΄ μ΅μ μ(PERSIST) ν΅ν΄μ λΆλͺ¨μ μμμ μλͺ μ£ΌκΈ°κ° λ§μΆ°μ Έ μμ΄μΌ νλ€.
CascadeType.REMOVE
@OneToMany
- λΆλͺ¨ μν°ν° μ κ±° μ λΆλͺ¨μ μμ λͺ¨λ μ€μ DBμμ μ κ±°λλ€.
- λΆλͺ¨ μν°ν°μ μμ μν°ν°μ μ°κ΄κ΄κ³κ° λμ΄μ§λλΌλ μ€μ DBμμ μ κ±°λμ§ μλλ€.
@ManyToOne
- λΆλͺ¨ / μμ μν°ν° μ κ±° μ μ°κ΄λ λΆλͺ¨, μμ λͺ¨λ μ€μ DBμμ μ κ±°λλ€.
- μ΄λ, μμ μν°ν°κ° μ¬λ¬ κ°μΌ κ²½μ° μΈλν€ μ μ½ μ‘°κ±΄ μλ°μΌλ‘ μΈν΄ μμΈκ° λ°μν μ μλ€.
- λΆλͺ¨ μν°ν°μ μμ μν°ν°μ μ°κ΄κ΄κ³κ° λμ΄μ§λλΌλ μ€μ DBμμ μ κ±°λμ§ μλλ€.
orphanRemoval = true
- λΆλͺ¨ μν°ν° μ κ±° μ λΆλͺ¨μ μμ λͺ¨λ μ€μ DBμμ μ κ±°λλ€.
- λΆλͺ¨ μν°ν°μ μμ μν°ν°μ μ°κ΄κ΄κ³κ° λμ΄μ§λ©΄ μμμ μ€μ DBμμ μ κ±°λλ€.