2023. 12. 29. 22:39ㆍ스프링
Entity 간의 관계를 연결 하는 법을 간단하게 배웠다..
관계를 설정 하는 것은 @OneToMany, @ManyToOne, @OneToOne 으로 1:N 관계는 @OneToMany,
그 와 반대 입장인 다일 땐, @ManyToOne을 사용 해준다.
M:N 관계의 경우, 테이블 관점에서 보면 사실은 1:N 관계와 N:1 관계로 이루어진 세 테이블과 동일합니다. JPA를 통해 직접 테이블 생성을 할때에만, @ManyToMany 어노테이션을 사용할 수 있는데, 중간의 N에 해당하는 테이블이 객체 상으로 표현되지 않기 때문에, 의도치 않은 쿼리가 나갈수 있다. 또한, Mapping 정보만 넣을 수 있고, 추가 정보를 넣지 못하므로 실무상으로는 사용하지 않는다고 한다.
@OneToMany(mappedBy = "course")
var lectures: MutableList<Lecture> = mutableListOf()
@ManyToOne
@JoinColumn(name = "course_id", nullable = false)
var course: Course
이런식으로 관계 설정을 엔티티에 해주면 된다. mappedBy를 사용함으로써 연관관계의 소유 주체가 누군지를 JPA에 알려준다. 이는 단방향 관계일 경우 필요하지 않지만 양방향 관계일 경우 반드시 필요하다고 한다. JPA에 입장에서는 서로가 관계 있다는 것을 알 수 있지만 FK를 어느 쪽에 소유하는지는 모르므로 이 키워드를 이용해, FK의 소유권을 포기해주어야 한다. 쉽게 정리하자면 1:N 관계에서는 보통 N 쪽이 연관관계의 주인이고, 1쪽에 mappedBy를 설정함으로써 연관관계의 주인을 JPA에 알려주는 것이다.
mappedBy 에 쓰는 “course” 와 같은 이름은 연관관계의 주인이 관계를 참조하는데 사용하는 멤버변수 명이다. Lecture에서 course 멤버 변수를 통해 관계를 참조하고 있다. @JoinColumn을 통해 명시적으로 외래키가 무엇인지 JPA에 알려준다. JPA로 테이블을 직접 생성해주는 경우에는 필요하지 않다.
일대다(OneToMany) 단방향 관계
장점
자식이 부모의 정보를 몰라도 된다면, 객체 지향적 관점에서 명확한 구조가 된다.
단점
update쿼리가 추가적으로 발생된다.
외래키(Foreign Key)에 NOT NULL 과 같은 제약조건이 걸려있다면 실패한다. (update 이전에 NULL 값으로 외래키를 insert 하므로)
주의점
@JoinColumn 을 필수로 입력해줘야한다.
다대일(ManyToOne) 단방향 관계
장점
외래키의 관리 주체가 데이터베이스와 일치하여, 데이터 일관성이 높아진다.
단점
역방향으로 연관된 Entity에 대한 참조가 없어, 역방향으로 접근하려면 별도의 쿼리가 필요한다.
주의점
@JoinColumn 을 필수로 입력해줘야한다.
양방향 관계
장점
두 Entity 간의 관계를 양측에서 접근할 수 있어, 쿼리 작성이 유연해진다.
단점
양방향을 모두 신경써야 하므로 관리해야할 부분이 더 많다.
주의점
연관관계의 주인을 필수로 표시해줘야한다. (mappedBy)
자식 Entity 저장시 부모 Entity까지 모두 입력해줘야한다. (Kotlin을 사용하면서, nullable하지 않게 부모(e.g. Course)를 표기한다면, 컴파일러가 이런 부분을 알려준다. null-safe하지 않은 Java는 매우 주의해야한다!!)
로딩 방식을 정하는 것은 Entity간의 연관성을 보고, 두 Entity가 연관성이 커 함께 조회되는 경우가 많다면 Eager Loading(즉시 로), 상대적으로 적다면 Lazy Loading(지연 로딩)을 사용하면 된다. fetch = FetchType.LAZY 혹은 fetch=FetchType.EAGER Option을 주는 식으로 말이다!
Entity 간의 연관 관계를 지정할 때, 설정을 통해 영속성 상태가 변경될때 이를 관계가 있는 다른 Entity에 전파할 수 있다. 이를 보통 Cascade라 칭한다.. 참고로 DDL에서도 외래키를 지정할때 이러한 CASCADE 옵션을 줄 수 있는데, 이와 비슷하지만 Application 레벨에서 이를 관리하는 점이 다르다고 한다.
Cascade의 경우, 부모 자식 관계에서 부모 Entity의 영속성을 전파할 때 쓰인다.
Course와 Lecture입장에서, 우리는 Course가 삭제되면 하위의 Lecture도 모두 삭제된다는 Policy를 설정하였으므로, Course를 삭제할 때 Lecture도 모두 삭제해줘야 한다. 하지만, Cascade를 통해 영속성을 전파한다면, 직접 삭제를 할 필요없이 JPA가 이 역할을 모두 대신해준다.
주요한 CASCADE 옵션은 아래와 같다.
CascadeType.ALL
모든 변경 작업(저장, 삭제, 갱신)을 전파한다. 자식 Entity가 부모 Entity의 생명주기를 따라간다.
CasecadeType.Persist
저장 작업을 전파한다. 즉, 부모 Entity를 저장할 때 자식 Entity도 함께 저장된다.
CasecadeType.Merge
병합 작업을 전파한다. 즉, 부모 Entity를 병합할 때 자식 Entity도 함께 병합된다.
CascadeType.REMOVE
삭제 작업을 전파한다. 부모 Entity를 삭제할 때 자식 Entity도 함께 삭제된다.
CascadeType.REFRESH
부모 Entity를 새로고침 할 때 자식 Entity도 함께 새로고침 된다.
CascadeType.DETACH
부모 Entity가 detach 될 때 자식 Entity도 Detached 상태가 되어 변경 사항 반영을 하지 않다.
실무에서는 주로, CascadeType.ALL, CasecadeType.Persist 를 많이 사용하게 된다.
JPA는 이를 단지 관계가 끊어진 걸로 보기에, 삭제는 일어나지 않다. 예를 들어 course가 지정되지 않은 lecture가 생겼다고 가정해보자. 이는, 우리의 Policy를 위반하는데,, 즉, 데이터의 무결성을 해친다고 할 수 있다.
이렇게 관계가 끊어졌을 때, 자식을 ‘고아 상태’ 라고 표현할 수 있는데, 이런 고아 상태의 자식을 제거할 수 있는 옵션이 있다. 바로 orphanRemoval로. @OneToMany 어노테이션에 설정을 하면, 관계가 끊어졌을시 JPA가 자동으로 DELETE 문이 호출하여 자식 데이터를 삭제해준다.
우와ㅏㅏ 연관관계 배웠는데 저런 어노테이션 있는 것을 첨 앎,,, ㅋㅋㅋㅋ
새로운 내용을 공부해서 재밌긴한데 아직 어렵긴하닼ㅋㅋ 아 당연한 소리를 한건가? 첨이 어렵긴 하니까,, ㅋㅋ
암튼 생각보다 유익하다 이런 정보를 습득한다는게,, 빨리 공부해보자ㅏㅏ
'스프링' 카테고리의 다른 글
TIL 20240105 (0) | 2024.01.05 |
---|---|
TIL20231231 (0) | 2023.12.31 |
TIL 20231228 (0) | 2023.12.28 |
TIL 20231227 (0) | 2023.12.27 |
TIL 20231226 (0) | 2023.12.26 |