database by narae :p

JPA ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ ํŒจํ„ด ๋ณธ๋ฌธ

๊ฐœ๋ฐœ ๋…ธํŠธ

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์œผ๋กœ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ํŒจํ„ด์„ ์ ์šฉํ•ด๋ณด์•˜๋‹ค.
  • ํ˜ธ๋ถˆํ˜ธ๊ฐ€ ๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํŒ€์›๋“ค์˜ ์˜๊ฒฌ์ด ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.
  • ํ˜น์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋Š” ์—†์„์ง€๋„ ์ด์•ผ๊ธฐ ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.