- 객체와 테이블 연관관계 차이
- 객체 참조와 테이블의 외래 키(주문 테이블 안 memberId와 같은)를 매핑
- 용어
- 방향(Drection): 단방향, 양방향
- 다중성(Multiplicity): N:1, 1:N, 1:1, N:M
- ⭐️연관관계의 주인(Owner): 객체 양방향 연관관계는 관리 주인이 필요
✔️ 시나리오
- 회원, 팀
- 회원은 하나의 팀에만 소속
- n(회원) : 1(팀)
📌 바로 저번 포스트에서 사용했던 프로젝트 말고 그 전 프로젝트인 ex1-hello-jpa
를 통해 실습
📌 주의
// JpaMain.java
try {
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeamId(team.getId());
em.persist(member);
tx.commit();
} catch (Exception e) {
객체를 테이블에 맞춰 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.
// JpaMain.java
try {
// 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
Member findMember = em.find(Member.class, member.getId());
Team findTeam = findMember.getTeam();
System.out.println("findTeam.getName() = " + findTeam.getName());
tx.commit();
} catch (Exception e) {
(이전 코드까지 단방향이기 때문에 getTeam은 됐어도 getMember는 안됐었다.)
(mappedBy
가 C의 포인터같은 존재,,🤤)
객체와 테이블 간 연관관계를 맺는 차이를 이해해야 한다.
객체의 양방향 관계는 사실 양방향 관계가 아닌 서로 다른 단방향 관계 2개이다.
객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.
class A {
B b;
}
class B {
A a;
}
테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN Member M ON T.TEAM_ID = M.TEAM_ID
양방향 매핑에서 주의해야 할 것은 연관관계의 주인이 누가 될 것이냐는 것이다.
이 경우를 가장 조심해야 한다.
연관관계의 주인에 값을 입력하지 않으면 아래와 같이 아무것도 들어오지 않는다. (읽기 전용에 값을 아무리 입력해봤자임)
양방향 매핑시 연관관계의 주인에 값을 입력해야 한다.
순수한 객체 관계를 고려하면 가장 아래와 같이 항상 양쪽 다 값을 입력해주도록 하자.
추가 설명
team.getMembers().add(member);
코드가 없다면 (순수하게 1차 캐시에만 들어가있는 상태에서)을 말하는 것
순수 객체 상태를 고려해 항상 양쪽에 값을 설정
연관관계 편의 메소드
// Member.java
public void changeTeam(Team team) { // (강사님: )이때 setTeam보다는 chageTeam처럼 set은 관례상에서만 사용하고 이렇게 중요한 메소드일때는 이름을 바꿔주는 편
this.team = team;
// this는 나 자신.
// 나 자신 인스턴스를 여기에 넣어주고
// JpaMain.java의 team.getMembers().add(member); 코드를 없애기
team.getMembers().add(this);
// 이렇게 해두면 멤버에 팀을 세팅하는 시점에 다 알아서 세팅이 되니까 실수를 줄일 수 있음
}
// JpaMain.java
member.changeTeam(team);
이렇게 하던가 아니면
// Team.java
public void addMember(Member member) {
member.setTeam(this);
members.add(member);
}
// Member.java
public void setTeam(Team team) {
this.team = team;
}
// JpaMain.java
team.addMember(member);
위 코드들을 모~두 쓰는 것은 헷갈리니까 하나만
→ 즉, 팀에서 멤버를 넣을건지 멤버에서 팀을 넣을건지를 결정해야 함.
단방향 매핑만으로도 이미 연관관계 매핑은 완료
(처음에 설계할 때 단방향 매핑으로 설계를 끝내야 함)
객체 입장에서 가급적 단방향 매핑이 좋은 것. 양방향 매핑은 신경써야 할 것이 많아 복잡도가 커짐.
양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
그럼 양방향 매핑은 왜 쓰냐?
JPQL에서 역방향으로 탐색할 일이 많음
단방향 매핑을 잘 해두면 양방향은 필요할 때 추가해도 됨(테이블에 영향을 주지 않기 때문)
비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안된다.
반드시 ⭐️ 연관관계의 주인은 외래 키의 위치를 기준으로 정하자.
실습은 jpashop으로 진행
// OrderItem.java
@Entity
public class OrderItem {
@Id @GeneratedValue
@Column(name = "ORDER_ITEM_ID")
private Long id;
@ManyToOne
@JoinColumn(name = "ORDER_ID")
private Order order;
@ManyToOne
@JoinColumn(name = "ITEM_ID")
private Item item;
private int orderPrice;
private int count;
상품 입장에서는 주문이 뭔지는 안 중요함.
// Member.java 코드 추가
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
// Order.java
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();