728x90
반응형
✅ 양방향 매핑이란?
- 객체의 연관관계 중 하나인 양방향 매핑은, 사실상 단방향 매핑이 두번 이루어진 것이다.
- 즉, 개념적으로 두 개의 단방향 매핑을 추상적으로 양방향 매핑이라 칭하는 것이다.
✅ 양방향 연관관계 매핑의 필요성
테이블과 객체를 비교해보자.
테이블에서는 외래 키 하나로 두 테이블의 연관관계를 확인할 수 있다.
- 외래 키를 가지고 조인하면 두 테이블간 데이터의 결합과 접근성이 자유롭다.
- 즉, 외래 키 하나만으로 한 컬럼의 연관된 데이터를 획득할 수 있다.
하지만 객체에서는?
- 객체를 탐방하려면 참조가 쌍방으로 존재해야 한다.
- 참조가 양방향으로 존재해야 참조와 역참조가 가능한 구조라는 것이다.
이런 패러다임의 차이를 극복하기 위해 양방향 매핑을 한다.
하지만, 엔티티 연관관계를 설정할 때 우선 단방향 매핑으로 구성하되, 비즈니스 로직상 역참조가 필요할 경우에 양방향 매핑을 해야 한다.
불필요한 양방향 매핑은, 로직의 복잡성을 증가시키고 문제 발생 시 원인을 찾기 어렵게 한다.
🌱 구체적으로 어떤 경우?
예를 들어..
Order
와OrderItem
이 존재할 때, 이는1:N
관계이므로 외래키가OrderItem
에 존재한다.- 하지만, 일반적으로 주문(
Order
)에서 주문한 상품의 목록(OrderItem
여러개)을 조회하는 일이 잦기에 이를List<OrderItem>
로 구현하고자 한다. - 위는 단순한 예시일 뿐이고, 우리는 단방향 매핑에서 사용했던 예제인
Member
와Team
의 경우를 이어서 학습한다.
✅ Member
& Team
🌱 엔티티 매핑
Member
는 동일하다.@Entity public class Member { @Id @GeneratedValue private Long id; @Column(name = "USERNAME") private String name; private int age; // @Column(name = "TEAM_ID") // private Long teamId; @ManyToOne @JoinColumn(name = "TEAM_ID") private Team team; ... }
Team
에는,Member
여러개를 관리하는 컬렉션List
를 추가한다.@Entity public class Team { @Id @GeneratedValue private Long id; private String name; @OneToMany(mappedBy = "team") List<Member> members = new ArrayList<member>(); ... }
🌱 엔티티 다루기
이를 통해 역방향으로 객체 그래프를 탐색할 수 있는 것이다.
// 조회 Team findteam = em.find(Team.class, team.getId()); int memberSize = findTeam.getMembers().size(); // 역방향 조회
✅ 연관관계의 주인
mappedBy= owner
의미 : “내 주인은owner
야”- 연관관계의 주인이라는 개념이 양방향 매핑에서는 필요하게 된다.
- 외래 키를 관리해줄 주체가 필요해진 것이다.
Member
레코드에서,TEAM_ID
의 변경 기준은 무엇일까?Member
객체의Team team
이 변경되었을 때?Team
객체의List members
이 변경되었을 때?
- 이 기준이 바로 주인(Owner)이다.
- 객체의 두 관계 중 하나를 연관관계의 주인으로 설정하면 된다.
🌱 양방향 매핑 규칙
- 연관관계의 주인만이 외래 키를 관리한다. (등록, 수정)
- 주인이 아닌 쪽은 읽기만 가능
- 주인은
mappedBy
사용 안함 - 주인이 아니면
mappedBy
로 주인 지정
🌱 연관관계의 주인 기준
연관관계의 주인은 외래키쪽으로
- 위의 말을 거의 공식처럼 외우는 경우가 많다.
- 이해를 하고 외워보자.
- 외래키가 있는 쪽은,
1:N
에서N
쪽이다. 역의 경우에서 모순을 찾아 증명해보자.
💎 외래키가 없는, 1:N
에서 1
쪽이 주인이라면..
- 1쪽이 주인이면,
Team
에 외래 키가 존재하고, 멤버 여러명이 한Team
에 속하는 구조일 것이다. Team
에 외래 키가 존재한다는 것은,Member
의PK
인memberId
를 갖는 레코드가 다수 있다는 뜻이 된다.- 그 뜻은
member
의 레코드 중 같은 팀인member
는Team
의PK
값이 같을 것이고, - 그럼 유일해야 하는
PK
의 값에 중복이 발생한다.
✅ 양방향 매핑 시 하는 실수
🌱 연관관계의 주인에 값을 입력하지 않는 경우
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
//역방향(주인이 아닌 방향)만 연관관계 설정
team.getMembers().add(member);
em.persist(member);
- 연관관계 주인이 아닌 쪽의 엔티티에 접근하여
.add()
를 한다. - 그렇게 되면 실제로 삽입된
member
레코드의TEAM_ID
필드 값은 null로 지정되게 된다.
그렇기에 연관관계의 주인에 값을 설정해야 한다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member); //연관관계의 주인에 값 설정
member.setTeam(team); //**
em.persist(member);
member.setTeam(team);
을 통해, 연관관계 주인의 컬럼에 값을 더해준 것을 볼 수 있다.- 이 코드가 있기 때문에,
team.getMembers().add(member);
코드는 사실상 주석처리를 해줄 수 있다. - 나중에 데이터베이스에 등록된
team
으로team.getMembers()
를 사용할 때, 지연로딩에 의해 이 컬럼(members
)과 연관된 값들에 대해SELECT SQL
을 한번 날리게 된다. - 그 때
members
에서TEAM_ID
가 같은 값들을 조회하며 갱신되는 것이다.
🌱 연관관계 편의 메서드
순수 객체 상태를 고려, 혹은 객체지향적인 특징에 잘 맞추기 위해 항상 양쪽에 값을 설정하자.
즉, 위의 예제를 빌어 말을 보충하자면
member.setTeam(team);
team.getMembers().add(member);
- 이 두 가지를 모두 해주자는 것이다!
하지만 두 가지 작업을 모두 해주는 것은 사람이 실수를 할 수도 있기에, 메서드로 따로 구현하여 빼먹는 일이 없도록 하면 더 좋다.
// 그 것을 연관관계 편의 메서드라 하며, 두 객체 중 한 쪽에만 구현하는 것이 관례이다. // team 내부의 함수. 멤버 변수로 List<member> members 존재. public void addMember(Member member) { members.add(member); member.setTeam(this); }
Ref
김영한 강사님, JPA 프로그래밍 - 기본편
감사합니다.
728x90
반응형
'Dev > Spring' 카테고리의 다른 글
연관관계 매핑(단방향) (0) | 2023.11.21 |
---|---|
변경 감지(Dirty Checking) (0) | 2023.11.20 |
JPA를 왜 쓸까? (0) | 2023.11.20 |
영속성 컨텍스트란 (0) | 2023.11.20 |
준영속과 변경감지 (0) | 2023.10.25 |