추후 업데이트될 수 있는 포스팅입니다.
Spring Boot에서 간단한 도메인을 만들다가 작성하는 포스팅입니다.
현재 데이터베이스로 관계형 데이터베이스를 쓰기 때문에 이를 객체와 매핑시키기 위해 JAVA 진영의 ORM 기술 표준인 JPA를 사용합니다.
객체가 연관관계를 다루는 방법과 테이블이 연관관계를 다루는 방법에는 차이가 있습니다.
객체는 객체의 주소를 참조해서 연관관계를 맺습니다.
그래서 도메인 A에서 도메인 B의 객체 주소를 참조하면, 도메인 A를 조회 시 B를 함께 조회할 수 있지만,
B에서는 A의 존재를 모릅니다.
이는 다시 말해 단방향의 연관관계를 맺는 것이라 말할 수 있습니다.
각 필드를 참조하고 JPA가 연관관계를 맺은 것을 알 수 있도록 애너테이션을 붙여야합니다.
일대다 관계의 경우,
'다'에 해당하는 도메인이 참조하는 객체에는 @ManyToOne을,
'일'에 해당하는 도메인이 참조하는 객체에는 @OneToMany를 붙여줍니다.
반면 테이블은 외래키를 이용해서 연관관계를 맺고 이 외래키를 이용하면 두 테이블 모두 조회가 가능합니다.
도메인A 테이블에서 외래키를 이용해서 도메인B를 조회할 수 있고, 도메인B에서도 외래키를 이용해 도메인A 테이블을 조회할 수 있습니다. A JOIN B 와 B JOIN A 모두 가능한 것이죠.
다시 말해 테이블은 양방향으로 매핑이 됩니다.
테이블은 하나의 외래키가 존재하고 이를 기반으로 두 테이블 간 연관관계를 관리합니다.
반면 엔티티는 한 클래스에서만 객체를 참조할 경우 단방향 연관관계를 맺는 것이고,
만약 양 방향으로 매핑하려면 각 클래스에서 서로를 참조하도록 필드를 생성하면 됩니다.
이때 둘중 어떤 관계 (단방향)를 중심으로 하나의 외래키를 관리해야할까요?
JPA에서는 이를 확실히 할 수 있도록 객체 관점에서 연관관계의 '주인'을 설정하여 JPA가 이를 확인하여 테이블에 변화를 주기로 합니다.
즉, 하나의 도메인을 기준으로 테이블의 외래키를 관리하도록 하는 것입니다.
여기서 기준이 되는 도메인을 연관관계의 주인이라고 합니다.
이 연관관계의 주인만으로 데이터베이스의 연관관계와 매핑되고, 외래키를 관리(등록, 수정, 삭제)할 수 있습니다.
주인이 아닌 쪽은 읽기만 가능합니다.
연관관계의 주인을 설정하는 기준이 정확히 있지는 않지만, 테이블 상 Foreign Key가 존재하는 테이블에 매핑되는 도메인으로 설정하겠습니다.
(책에서 언급하기를, FK가 존재하는 테이블에 매핑되는 도메인이 외래키를 관리하는 것이 좋다고 합니다.)
데이터베이스 테이블의 다대일, 일대다 관계에서는 '다'에 해당하는 테이블에 외래키가 존재합니다.
그래서 도메인에서도 @ManyToOne이 붙는 필드를 갖고있는 도메인, 즉 '다'에 해당하는 도메인이 연관관계의 주인이 됩니다.
그렇기 때문에 @OneToMany에만 mappedBy 속성이 있습니다. (해당 속성으로 연관관계의 주인이 아닌 클래스에서 '주인이 되는 필드명'을 지정하는 것입니다.)
Member라는 도메인과 Order라는 도메인이 있다고 합시다.
Member와 Order 간의 관계는 1대 N입니다. (한 고객이 여러 주문을 할 수 있으니까요!)
대충 두 도메인의 필드를 적어보면 다음과 같습니다.
Member
- long타입 id
- Sting타입 name
- Address타입 address
- List<Order> orders
Order
- long타입 id
- Member타입 member (FK)
- LocalDateTime타입 orderDate
- 열거형타입 status
위 모습과 달리, 관계형 데이터베이스 관점에서
Member 테이블에는 orders 필드와 같은 칼럼이 없고,
Orders 테이블에만 Foreign key로 member_id 칼럼이 있습니다.
이처럼 연관관계를 맺는 도메인(객체 관점) 사이에는 양방향으로 참조하고 있지만
테이블 사이에는 그렇지 않습니다.
이로인해 극단적으로는, Member의 List타입의 orders 필드에는 변화가 없는데 Order에만 변화가 생긴 경우, 무엇을 믿고 트랜잭션을 던져야할지 말지 모르는 상황이 발생할 수 있습니다.
그럼, Foreign key가 있는 Orders를 연관관계의 주인으로 설정해봅시다.
연관관계의 주인이 아닌 도메인의 참조 필드에 mappedBy 속성을 통해 지정해주면 됩니다.
class Member {
@OneToMany(mappedBy="member")
private List<Order> orders = new ArrayList<>();
// 생략
}
class Order {
@ManyToOne
@JoinColumn(name="member_id")
private Member member;
// 생략
}
결론적으로, 연관관계의 주인이 외래 키를 관리하고, 그 반대편은 읽기만 가능할 뿐 외래 키를 변경하지 못하게 됩니다.
그럼 1대1의 관계를 맺는 도메인 사이에서는 어디에 Foreign Key(연관관계의 주인)을 두어야 할까요?
주문 도메인 Order와 배송 도메인 Delivery를 생각해봅시다. 주문 1개당 배송 정보는 1개 연결되므로, 주문과 배송은 1대 1 관계입니다.
이때는 어느 쪽에 둘지 정하면 되는데, 기본적으로 더 자주 조회되는 도메인에 FK를 두는 방법이 있습니다.
결국, Delivery보다 Order가 더 자주 조회(select)되므로 Orders 테이블에 Foreign key를 두고 이와 매핑되는 Order 도메인이 연관관계의 주인이 됩니다.
'백엔드 개발하며 작성한 > 데이터베이스' 카테고리의 다른 글
[ORM] 상속 관계 매핑 (0) | 2022.05.20 |
---|---|
[ORM > JPA] 영속성 컨텍스트 (0) | 2022.04.14 |
[MySQL]날짜/시간 타입과 TIMESTAMP 칼럼 생성 (0) | 2021.10.08 |
[MySQL Workbench] ERD를 SQL 코드로 변환하기 (0) | 2021.09.02 |
cmd로 MongoDB에 데이터 저장하기 (0) | 2021.05.20 |