일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 프로젝트
- 정수론
- n과 m
- MYSQL
- 프로그래머스
- 알고리즘
- 브루트포스 알고리즘
- Spring Security
- 자료 구조
- Vue
- 너비 우선 탐색
- 백트래킹
- 배포
- 소수 판정
- 문자열
- JPA
- DB
- dfs
- SWEA
- 다이나믹 프로그래밍
- 구현
- 스택
- 수학
- 깊이 우선 탐색
- springboot
- 그래프 이론
- 백준
- 재귀
- 정보처리기사
- 그래프 탐색
- Today
- Total
영원히 남는 기록, 재밌게 쓰자
다대일(N:1) 연관 관계에서 양방향 단방향 매핑과 연관 관계의 주인에 대해서 알아보자. 본문
모든 연관 관계에는 단방향과 양방향이 존재한다.
연관 관계 매핑의 종류에는
- 다대일
- 일대다
- 일대일
- 다대다
크게 4가지 종류로 분류된다.
연관 관계에서는 중요한 것은 시나리오 룰을 정하는 것이 중요하다.
단방향 매핑
예제의 룰은 회원과 팀이 있고 회원은 하나의 팀을 가질 수 있고 팀은 여러 회원을 가질 수 있다고 가정했다.
💡 예제에서는 다대일 관계일 때 단방향과 양방향 관계를 어떻게 설정하는지 다루려고 한다.
테이블의 입장으로 보았을 때는 회원과 팀은 다대일 관계를 가진다.
객체 입장에서는 테이블과 연관 관계 페러다임을 일치시켜주는 것이 좋다.
그 입장으로 보았을 때 객체를 테이블에 맞추어 모델링을 하는 경우를 생각해보면 위와 같이 Member에는 teamId 필드를 추가할 수 있다.
이렇게 되면 member.setTeamId(team.getId())와 같이 id를 가져와서 저장을 할 수 있는데 조회하는 입장을 생각해보면 멤버에서 팀을 조회하면 팀 id만 조회가 가능한데 팀의 전체 정보를 알고 싶으면 다시 팀을 조회하여야 한다.
이는 객체 지향 관점에서 보았을 때 좋은 설계 방법이 아니다.
객체를 테이블에 맞추어서 데이터 중심으로 모델링을 할 경우 객체와 테이블 간 협력관계를 만들기 어렵다.
그럼 객체 지향 관점에서 객체 연관 관계를 사용한 모델링을 보면 테이블과 같이 team_id가 아닌 Team객체를 필드로 추가해준다.
그 다음 객체와 테이블 간 연관관계 매핑을 해주어야 한다. 이는 @JoinColumn 어노테이션으로 가능하다.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
@Column(name = "username")
private String name;
@ManyToOne
@JoinColumn(name = "team_id") // 조인 해야하는 컬럼이 뭐냐!
private Team team;
...
@JoinColumn은 해당 필드가 연관 관계를 매핑할 때에 사용할 테이블의 컬럼을 선택하는 기능을 한다.
그리고 이는 테이블에서 지정한 외래키와 직접적으로 연관관계를 맺어 수정이나 추가 시 해당 외래키 컬럼이 있는 테이블의 수정과 추가가 일어난다.
이렇게 하면 위와 같은 연관관계가 성립된다.
하지만 Team 입장에서 Member 를 참조할 일이 발생해서 Member 를 조회할 일이 생긴다면 어떻게 될까?
양방향 매핑
테이블의 입장에서 본다면 의미가 없다. 참조하고 있는 외래키 하나만 있으면 조인을 통해 반대 테이블에서도 바로 원하는 데이터를 가져올 수 있기 때문이다.
하지만 객체 입장에서 보았을 때는 팀에서 회원 알 수 있는 방법이 없다. 참조하고 있는 데이터가 아무것도 없기 때문이다.
그러면 객체 입장에서 양방향 연관 관계를 만들어주기 위해서는 회원들 정보를 저장할 수 있는 컬렉션 필드가 하나 필요하다.
Team 객체에서는 필드를 추가하면서 다음과 같이 연관관계를 매핑하여 준다. Member 엔티티 코드와 함께 보자.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
@Column(name = "username")
private String name;
@ManyToOne
@JoinColumn(name = "team_id") // 조인 해야하는 컬럼이 뭐냐!
private Team team;
...
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany(mappedBy = "team") // 내 반대편 사이드의 team이라는 변수와 매핑이 되어있어
List<Member> members = new ArrayList<>();
...
팀 입장에서는 일대다이므로 @OneToMany 를 사용해야 한다. 속성 값으로 준 mappedBy 속성을 지정하면 읽기만 가능하다.
그러면 @JoinColumn 이 members 필드에 왜 없는지, 테이블의 연관 관계 매핑처럼 매핑을 해주어야 되지 않을까 생각할 수 있다.
사실 @JoinColumn 을 members 에 지정해주어서 members를 연관관계의 주인으로 만들어주어도 된다.
하지만 이렇게 했을 때 애매한 부분이 발생한다. 이렇게 되면 팀이 연관관계의 주인이 되고 멤버의 필드는 mappedBy 속성으로 읽기만 가능해진다. 그러면 추가, 수정과 같은 행위를 Team 에서 해주어야 한다.
그럼 테이블 상에서 외래키는 Member 쪽에 있기 때문에 Team 을 수정했는데 Member 테이블과 관련된 쿼리가 날아가게 된다.
정리
- 단방향과 양방향 매핑 관계가 존재하는데 최대한 단방향 매핑을 사용하고 반대쪽에서 조회할 일이 많이 발생할 때에 양방향 매핑을 고려하자.
- 연관 관계 주인은 항상 외래키의 위치를 기준으로 정하자
자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 - 인프런
저는 야생형이 아니라 학자형인가봐요^^ 활용편 넘어갔다 30% 정도 듣고 도저히 답답해서 기본편을 들어버렸네요^^. 한주 한주 김영한님 강의 들으니 렙업되는 모습을 스스로 느낍니다. 특히 실
www.inflearn.com
'springboot > JPA' 카테고리의 다른 글
API 개발 공부 - 지연 로딩과 조회 성능 최적화에 대해서 (0) | 2024.05.26 |
---|---|
queryDSL을 사용해서 페이징 처리 해보기 (0) | 2024.04.20 |
프록시 (0) | 2024.02.27 |
영속성 컨텍스트의 플러시(flush)에 대해서 (0) | 2024.02.21 |
영속성 컨텍스트 (0) | 2024.02.20 |