개발 λ…ΈνŠΈ

JPA μ—°κ΄€ 관계가 μžˆλŠ” μ—”ν‹°ν‹° 생성 νŒ¨ν„΄

dbbymoon 2023. 3. 9. 04:48

Objective

πŸ’‘ JPA μ—°κ΄€ 관계가 μžˆλŠ” μ—”ν‹°ν‹°λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ—°κ΄€ μ—”ν‹°ν‹°λ₯Ό var nullable νƒ€μž…μœΌλ‘œ μ„ μ–Έν•˜μ—¬ 생성해야 ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ λ°μ΄ν„°λ‘œ λ³΄μ•˜μ„ λ•Œ ν•΄λ‹Ή μ—”ν‹°ν‹°λŠ” var νƒ€μž…λ„ nullable νƒ€μž…λ„ μ•„λ‹ˆκΈΈ μ›ν•˜λŠ” 경우 val non-nullable νƒ€μž…μœΌλ‘œ μ„€μ •ν•˜κ³  JPA μ—”ν‹°ν‹°λ₯Ό μƒμ„±ν•˜λŠ” νŒ¨ν„΄μ— λŒ€ν•΄ κ³΅μœ ν•©λ‹ˆλ‹€.

 

Background

OrderEntity.kt

OrderProductEntity.kt

OrderService.kt

@Service
class OrderService(
	private val orderRepository : OrderRepository
) {
	fun saveOrder(param : OrderCreateParam) {
		val order = OrderEntity(
				orderId = param.orderId
		)
		val orderProductList = param.orderProductList.map {
				OrderProductEntity(
						order = order
				)
		}
		order.orderProductList = orderProductList

		orderRepository.save(order)
	}
}
  • OrderEntity λ₯Ό λ¨Όμ € μƒμ„±ν•˜κ³ , λ¨Όμ € μƒμ„±ν•œ Order와 ν•¨κ»˜ OrderProductEntityλ₯Ό μƒμ„±ν•˜μ—¬ orderProductListλ₯Ό set ν•΄μ•Ό ν•œλ‹€.
  • 번거둜운 것은 λ‘˜μ§ΈμΉ˜κ³ , orderProductList의 νƒ€μž…μ΄ varμ΄λΌμ„œ μ–Έμ œ μ–΄λ””μ„œ λ°”λ€” 수 μžˆλ‹€λŠ” 단점이 μžˆλ‹€.

⇒ κ·Έλž˜μ„œ μ €λŠ” orderProductListλ₯Ό val둜 μ‚¬μš©ν•˜κ³  μ‹Άμ—ˆμŠ΅λ‹ˆλ‹€..

 

 

μ•Œκ³  μžˆλŠ” λ°μ΄ν„°λ“€λ§Œ 가지고 μƒμ„±μžλ₯Ό ν˜ΈμΆœν•˜κ³ , init μ—μ„œ μ²˜λ¦¬ν•˜μž

CreateOrderParam.kt

data class CreateOrderParam(
    val userId: Long,
    val orderProductList: List<CreateOrderProductParam>
)

data class CreateOrderProductParam(
    val productId:  Long
)
  • μƒμ„±μž νŒŒλΌλ―Έν„°λ‘œ λ„˜κΈΈ ν΄λž˜μŠ€μž…λ‹ˆλ‹€. 이 클래슀만 가지고 Service μ½”λ“œμ—μ„œ OrderEntityλ₯Ό 생성할 κ²ƒμž…λ‹ˆλ‹€.

OrderService.kt

@Service
class OrderService(
	private val orderRepository : OrderRepository
) {
	fun saveOrder(param : CreateOrderParam) {
		val order = OrderEntity(param)
		orderRepository.save(order)
	}
}
  • μ„œλΉ„μŠ€ μ½”λ“œλŠ” μ΄λ ‡κ²Œ 될 κ²ƒμž…λ‹ˆλ‹€.

OrderEntity.kt

@Entity
@Table(name = "ORDER")
class OrderEntity(createOrderParam: CreateOrderParam) {
    @Id
    @Column(name = "ORDER_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val orderId: Long? = null

    @OneToMany(
        cascade = [CascadeType.ALL],
        fetch = FetchType.LAZY,
        mappedBy = "order",
        targetEntity = OrderProductEntity::class
    )
    val orderProductList: List<OrderProductEntity>

    @Column(name = "USER_ID")
    val userId: Long

    init {
        this.userId = createOrderParam.userId
        this.orderProductList = createOrderParam.orderProductList.map {
            OrderProductEntity(
                createOrderProductParam = it,
                order = this
            )
        }
    }
}
  • OrderEntity 의 init μ—μ„œ ν”„λ‘œνΌν‹°λ₯Ό μ΄ˆκΈ°ν™”ν•˜κ³ , OrderProductEntityλ₯Ό 생성할 λ•Œ orderλ‘œλŠ” this ν‚€μ›Œλ“œλ₯Ό 톡해 OrderEntityλ₯Ό λ„˜κΉλ‹ˆλ‹€.

OrderProductEntity.kt

@Entity
@Table(name = "ORDER_PRODUCT")
class OrderProductEntity(
    createOrderProductParam: CreateOrderProductParam,
    order: OrderEntity,
) {
    @Id
    @Column(name = "ORDER_PRODUCT_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val orderProductId: Long? = null

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ORDER_ID", referencedColumnName = "ORDER_ID")
    val order: OrderEntity

    @Column(name = "PRODUCT_ID")
    val productId: Long

    init {
        this.order = order
        this.productId = createOrderProductParam.productId
    }
}
  • OrderProductEntity μ—μ„œλŠ” μƒμ„±μžμ—μ„œ 받은 OrderEntity둜 orderλ₯Ό μ΄ˆκΈ°ν™”ν•΄μ€λ‹ˆλ‹€.

 

μ½”ν‹€λ¦°μ˜ init

  • μƒμ„±μžλ₯Ό 톡해 μΈμŠ€ν„΄μŠ€κ°€ λ§Œλ“€μ–΄μ§ˆ λ•Œ ν˜ΈμΆœλ˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€.
  • μΈμŠ€ν„΄μŠ€κ°€ λ§Œλ“€μ–΄μ§ˆ λ•Œ, init λ‚΄λΆ€μ˜ μ½”λ“œλ₯Ό 톡해 ν”„λ‘œνΌν‹°λ₯Ό μ΄ˆκΈ°ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ—¬κΈ°μ„œ λ°œμƒν•  수 μžˆλŠ” λ¬Έμ œκ°€ μžˆλŠ”λ°μš”…

  • init λ‚΄λΆ€μ—μ„œ μ΄ˆκΈ°ν™”ν•˜λŠ” ν”„λ‘œνΌν‹°λ₯Ό final 둜 μ„€μ •ν•˜λΌλŠ” 문ꡬ가 λœΉλ‹ˆλ‹€.
    • μ°Έκ³  링크
    • 이 경우 Derived μ—μ„œλŠ” NPE κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • Base의 init μ½”λ“œλ₯Ό μˆ˜ν–‰ μ‹œ size ν”„λ‘œνΌν‹°λ₯Ό μ‚¬μš©ν•˜λŠ”λ°, Derived의 sizeλŠ” items.size 둜 μ΄ˆκΈ°ν™”ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 이 λ•Œ, itemsλŠ” μ΄ˆκΈ°ν™”κ°€ λ˜μ§€ μ•Šμ€ μ‹œμ μ΄λ‹ˆ NPEκ°€ λ°œμƒν•©λ‹ˆλ‹€.⇒ λ”°λΌμ„œ, open class의 init λ‚΄λΆ€μ—μ„œ μ ‘κ·Όν•˜λŠ” μ–΄λ–€ ν”„λ‘œνΌν‹°λŠ” ν•˜μœ„ ν΄λž˜μŠ€μ— μ˜ν•΄ μ‘°μž‘λ˜μ–΄ μ˜ˆμ™Έκ°€ λ°œμƒν•  수 μžˆλŠ” 상황듀이 μžˆμ–΄ final ν”„λ‘œνΌν‹°λ‘œ μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.
open class Base {
    open val size: Int = 0
    init { 
				println("size = $size") 
		}
}

class Derived : Base() {
    val items = mutableListOf(1, 2, 3)
    override val size: Int get() = items.size
}

 

κΈ°λ³Έ μƒμ„±μžμ— μ»¬λŸΌμ— λŒ€μ‘ν•˜λŠ” ν”„λ‘œνΌν‹°λ“€μ΄ 없어도 될까?

  • HibernateλŠ” 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 λ•Œ reflection 기법을 μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό μ΄ˆκΈ°ν™” ν•œλ‹€.
  • ν•„λ“œμ™€ μ»¬λŸΌμ„ λ§€ν•‘ν•˜λŠ” @Column μ–΄λ…Έν…Œμ΄μ…˜μ„ 톡해 맀핑할 수 있기 λ•Œλ¬Έμ— κΈ°λ³Έ μƒμ„±μžμ— κΌ­ μ»¬λŸΌμ— λŒ€μ‘ν•˜λŠ” ν•„λ“œλ“€μ΄ μžˆμ–΄μ•Ό ν•˜λŠ” 것이 μ•„λ‹™λ‹ˆλ‹€.

 

κ²°λ‘ 

  • μ—°κ΄€ 관계가 μžˆλŠ” μ—”ν‹°ν‹°λ₯Ό 생성할 λ•Œ μ—°κ΄€ μ—”ν‹°ν‹°λ₯Ό κΌ­ var nullable type으둜 μ„€μ •ν•˜μ§€ μ•Šμ•„λ„ 생성할 수 μžˆλŠ” νŒ¨ν„΄μ„ μ μš©ν•΄λ³΄μ•˜λ‹€.
  • ν˜ΈλΆˆν˜Έκ°€ 갈릴 수 있기 λ•Œλ¬Έμ— νŒ€μ›λ“€μ˜ 의견이 κΆκΈˆν•©λ‹ˆλ‹€.
  • ν˜Ήμ‹œ λ°œμƒν•  수 μžˆλŠ” λ¬Έμ œλŠ” 없을지도 이야기 ν•΄μ£Όμ‹œλ©΄ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€.