Summary:
DB의 연관관계 방식은 JPA에서의 방식과 차이가 있음 : FK로 관리 vs 다른 엔티티 참조
이를 일치시키기 위해서 양방향 관계에서는 mappedBy로 연관관계의 주인을 설정해줘야함
추가로, 객체의 관점에서 일관성을 유지하기 위해
주인 엔티티만 수정할 뿐 아니라 상대 엔티티도 수정할 필요 있음 → 연관관계 편의 메서드
📌 DB와 JPA의 연관관계 차이점
- 데이터 베이스에서는 하나의 외래키를 이용해 두 테이블을 join 하는 구조
- JPA에서는 한 엔티티가 다른 엔티티를 참조하는 구조
이런 JPA의 특성 때문에 '방향성'이 생기는 것 => 단방향과 양방향 - 단방향 : 하나의 엔티티가 다른 엔티티를 참조하는 것
- 양항뱡 : 각 엔티티가 서로의 엔티티를 참조하는 것 (단방향 2개)
정리하자면,
DB : 외래키(FK)를 한 테이블에 저장
JPA : 엔티티가 다른 엔티티를 참조
📌 연관관계의 주인이 필요한 이유
- JPA와 데이터베이스와 패러다임을 맞추기 위해서는
참조의 관계에서 어떤 테이블에 FK를 저장할지를 명확히 해줘야 함 - 단방향의 경우, 다른 엔티티를 참조하는 엔티티에 외래키가 저장되는게 명확하지만
양방향의 경우 어떤 테이블에서 외래키를 저장할지 명확하지 않음 - 🤔 그냥 두 테이블 모두 FK를 저장하면 안되나?
두 테이블에서 모두 FK를 저장하는 것은
연관 관계 관리 포인트가 두 개이므로 혼란을 주게 됨 - 따라서 연관관계의 주인을 정해서 '연관관계의 주인을 통해서만 FK를 수정하겠다!'고 정해야 함
- 참고 블로그(강추) :
JPA 연관 관계 한방에 정리 (단방향/양방향, 연관 관계의 주인, 일대일, 다대일, 일대다, 다대다) (tistory.com)
📌 연관관계 주인의 역할
- 연관 관계의 주인은 두 객체 사이에서 조회, 저장, 수정, 삭제를 할 수 있지만,
연관 관계의 주인이 아니면 조회만 가능 - 또한, 주인이 아닌 곳에서 아무리 객체를 변경해 보아봤자 데이터베이스에 적용이 되지 않음
참고 유튜브 : https://www.youtube.com/watch?v=brE0tYOV9jQ
📌 mappedBy
- 연관관계의 주인을 지정해주는 옵션
다음 문장을 실행한다고 할 수 있음 - => "나는 내 연관관계의 주인의 [~~~] 필드에 해당해!"
- mappedBy를 사용하지 않으면 다대일 관계의 경우 중간 테이블이 생성되며,
일대일 관계의 경우 각각의 테이블에 서로를 참조하는 FK가 설정됨
실험 결과 : https://ttl-blog.tistory.com/126#%EC%98%88%EC%8B%9C-1
📌 연관 관계의 주인만 신경쓰면 되나?
'연관관계의 주인에만 접근'하는 원칙만 지키면 되는가?는 DB 관점에서는 맞음
하지만 객체의 관점에서는 그것만으로는 부족함
예를들어,
class SiteUser {
@OneToMany(mappedBy="siteUser")
List<Post> postList
}
와 같은 필드가 있을 때, 이는 mappedBy 옵션 때문에 DB에 저장되지 않지만,
객체 자체로 봤을 때는 Post 엔티티와의 매핑 포인트가 되는 중요한 필드
이때 '사용자가 게시글을 작성하는 기능'을 구현하는 경우
Post 객체가 User를 참조하게 하여 저장하게 하면 DB 상으로는 문제가 되지 않음
하지만 객체의 관점에서는 User의 postList에 새로운 Post를 추가까지 해줘야 매핑이 완성됨!
그렇지 않으면 '사용자가 작성한 post를 저장하는 postList 필드'라는 일관성이 깨지는 문제가 발생함
문제 발생 코드 참고할 수 있는 블로그 :
https://velog.io/@conatuseus/%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91-%EA%B8%B0%EC%B4%88-2-%EC%96%91%EB%B0%A9%ED%96%A5-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84%EC%99%80-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84%EC%9D%98-%EC%A3%BC%EC%9D%B8
📌 (생각보다 더 중요한) 연관관계 편의 메서드
- 일관성을 위해서 '연관관계의 양쪽 객체를 업데이트하는' 메서드를 만들어줄 필요가 있음
- 이때 연관관계 편의 메서드의 위치는 중요 X
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE)
List<Comment> commentList = new ArrayList<>();
}
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(cascade = CascadeType.REMOVE)
private Post post;
// 연관관계 편의 메서드
public void addCommentTo(Post post){
this.post = post;
if(post.getCommentList() == null){
post.setCommentList(new ArrayList<>());
}
post.getCommentList().add(this);
}
}
'Spring Boot' 카테고리의 다른 글
[SpringBoot] 테스트 코드 가독성 높이기 - BDDMockito, assertj (0) | 2023.09.29 |
---|---|
[SpringBoot] @Mock vs @MockBean (0) | 2023.09.29 |
스프링 시큐리티를 이용한 로그인 (0) | 2023.09.03 |
스프링 시큐리티를 이용한 회원가입 (0) | 2023.09.02 |
트랜잭션의 개념과 @Transacional 어노테이션 (0) | 2023.08.11 |