작성
·
433
2
안녕하세요.
Sto라는 부모 엔티티가 있습니다.
자식으로는 Realestate 라는 엔티티가 있습니다.
해당 엔티티의 관계는 Sto + Realestate 이렇게 1개의 쌍을 이루어 생성이 됩니다.
저는 @OneToOne 양뱡향 관계를 사용하고 있습니다.
cascade 속성을 이용하여, Sto를 save 했을 때 하위 엔티티까지 함께 저장하려고 하고 있습니다.
이때, 모든 엔티티를 val를 사용하려고 했는데 Sto 생성자 호출 시 하위 엔티티에서 Sto 엔티티의 값을 알 수가 없어서 생성이 불가한 상태입니다.
물리적으로 Sto + Realestate는 한쌍인데 방법이 없을까 싶어 문의드립니다.
var + null을 사용하면, 어떻게든 할 수 있을 것 같은데 양방향 설정을 위해 이렇게 풀어서 사용해야 하나 궁금하네요..
@Entity
@Table(name = "sto")
class Sto(
@Id
@Column(name = "sto_id", nullable = false)
private val id: String,
@OneToOne(
mappedBy = "sto",
cascade = [CascadeType.PERSIST]
)
val realestate: Realestate,
@OneToOne(
mappedBy = "sto",
cascade = [CascadeType.PERSIST]
)
val youtube: Youtube,
)
@Entity
class Realestate(
@Id
@Column(name = "realestate_id", nullable = false)
private val id: String,
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sto_id", nullable = false)
val sto: Sto,
)
fun main() {
Sto(
id = "sto_id",
realestate = Realestate(
id = "realestate_id",
sto = 여기가 문제네요..
)
)
}
답변 1
1
안녕하세요, 민영님! 좋은 질문 감사드립니다! 🙏 결론부터 말씀드리면 방법은 있습니다... 예시로 코드를 조금 간소화 해서 A / B 클래스로 보여드리겠습니다.
@Entity
class A(
@Id
val id: String,
bId: String,
) {
@OneToOne(
mappedBy = "a",
cascade = [CascadeType.PERSIST] // [주의 2]
)
val b: B = B(bId, this) // [주의 1]
}
@Entity
class B(
@Id
private val id: String,
@OneToOne(fetch = FetchType.LAZY)
val a: A,
)
코드로 느낌이 오시겠지만, 방법을 말씀드리자면,
실제 프로퍼티 선언은 class body에 하고
val b: B = B(bId, this)
B 클래스에서 필요한 필드를 생성자에 넘겨, B 인스턴스 자체를 A class body 안에서 만드는
방법입니다!
만약 1 : N 관계라면, class body에 MutableList를 만들고 부모 클래스를 인스턴스화 한 다음 자식 클래스들을 addAll
함으로써 nullable하지 않게 코드를 작성할 수 있습니다.
다만 제가 주의 1, 2를 적어두었는데요!
[주의 1]
클래스 A는 Entity 이고, Entity 클래스일 경우 보통 allOpen 설정을 해두니 final 클래스가 아닙니다.
final이 아닌 코틀린 클래스를 인스턴스화 하는 과정에서 this
를 사용하면 위험할 수 있는데요!
(관련 강의 : <자바 개발자를 위한 코틀린 입문 : 10강>)
(관련 문서 : https://kotlinlang.org/docs/inheritance.html#derived-class-initialization-order)
현재 코드는 A
클래스의 하위 클래를 인스턴스화 하고 있지도 않고
JPA가 데이터를 가져와 Entity를 만들 때는 기본적으로 필드에 직접 값을 넣어주기에
문제될 부분이 없지만, IDE에서는 경고를 주고 있어 찜찜하실 수 있습니다. Hibernate 버전이 올라가면서 2번의 동작이 변경될 수도 있고요!
[주의 2]
이 부분은 Kotlin과 관련된 부분은 아니고 Entity에서 @Id
를 not null String으로 갖고 있는 경우 발생할 수 있는데요!
PERSIST
만 casecade 옵션에 주신 것은
최초 save
하는 과정에서 새로운 Entity
이면 PERSIST
를 사용하기 때문
으로 생각됩니다.
다만, 여기에 최대 함정이 있는데요! isNew(entity)
에서 not null String은 false
가 나온다는 것입니다!!! 즉, SimpleJpaRepository.save
메소드는 isNew
를 하는 과정에서 PK를 확인하는데, PK 값이
null 이면 새로운 Entity
숫자타입이고 0이면 새로운 Entity
위의 두 경우 모두 아니다~ 그럼 새롭지 않은 Entity
로 판단해버려요!
관련 로직은 AbstractEntityInformation
에서 확인해보실 수 있습니다.
따라서, 사실상 DB에 영속화 되지 않은 새로운 Entity이더라도, 보내주신 것처럼 PK가 String으로 미리 설정되어 있다면, MERGE
까지 cascade 옵션을 켜주셔야 의도하시는 동작이 가능할 겁니다!
아이고~ 글이 너무 길었네요! 요약 드려보겠습니다!
JPA + 코틀린으로 원하시는 바 가능하긴 합니다! 다만 사람에 따라 살짝 찜찜할지도...
Cascade 옵션에 PERSIST
말고 MERGE
를 함께 주셔야할 수 있어요!
궁금한 점이 해소되셨으면 좋겠습니다~ 감사합니다!! 🙇🙇
아이고~~ 여쭤봐주셔서 감사해요!!! ☺️☺️
넵넵! 현재
코틀린 고급편
제네릭 / 위임, 지연 / 복잡한 함수형 프로그래밍
DSL / 어노테이션 및 리플렉션 / 기타 꿀팁
2시간 안에 끝내는 코루틴
2가지 강의를 준비중에 있습니다! ☺️
두 주제를 하나로 합쳐 오픈 할까 하다가, 강의 가격도 더 낮추고, 원하시는 파트만 집중해서 보실 수 있게끔 분리하는 선택을 해보았습니다.
두 강의 모두 늦어도 8월 말까지는 선보일 수 있도록, 열심히 준비해보겠습니다.
감사합니다!! 🙇
궁금한 점이 해소되는 답글이였습니다!
가끔 질문할 때마다 좋은 답변을 제시해주셔서 감사합니다~!
추후 강의 계획이 있다면 궁금하네요!
코틀린 심화[코루틴 등등]!