[JPA] 연관관계 매핑 기초

윤경·2021년 10월 12일
0

JPA

목록 보기
6/22
post-thumbnail
post-custom-banner
  • 객체와 테이블 연관관계 차이
  • 객체 참조와 테이블의 외래 키(주문 테이블 안 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) {

객체를 테이블에 맞춰 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.

  • 테이블은 외래 키로 조인을 사용해 연관된 테이블을 찾는다.
  • 객체는 참조를 사용해 연관된 객체를 찾는다.
  • 테이블과 객체 사이에는 이런 큰 간격이 있다.


[1] 단방향 연관관계

// 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) {


[2] ⭐️ 양방향 연관관계와 연관관계의 주인 1- 기본

양방향 매핑

(이전 코드까지 단방향이기 때문에 getTeam은 됐어도 getMember는 안됐었다.)

list 추가 후

연관관계 주인과 mappedBy

(mappedBy 가 C의 포인터같은 존재,,🤤)
객체와 테이블 간 연관관계를 맺는 차이를 이해해야 한다.

객체와 테이블이 관계를 맺는 차이

  • 객체 연관관계 = 2개
    - 회원 → 팀 (단방향 연관관계 1개)
    - 팀 → 회원 (단방향 연관관계 1개)
    ➡️ 단방향 연관관계가 2개 있는 것
  • 테이블 연관관계 = 1개
    - 회원 ⬅️➡️ 팀 (양방향 연관관계 1개)
    (사실은 방향이 있다기 보다 하나만 있으면 왔다갔다 가능한 것)

객체의 양방향 관계

객체의 양방향 관계는 사실 양방향 관계가 아닌 서로 다른 단방향 관계 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

외래 키 관리는 누가? 🤔

연관관계의 주인 (Owner)

양방향 매핑에서 주의해야 할 것은 연관관계의 주인이 누가 될 것이냐는 것이다.

  • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 한다.
  • 연관관계의 주인만이 외래 키를 관리(등록, 수정)할 수 있다.
  • 주인이 아닌 쪽은 읽기만 가능하다.
  • 주인은 mappedBy 속성을 사용할 수 없다.
  • 주인이 아니면 mappedBy 속성으로 주인을 지정한다.

외래키가 있는 곳을 주인으로


[3] 양방향 연관관계와 연관관계의 주인 2 - 주의점, 정리

📌 양방향 매핑시 가장 많이 하는 실수 - 연관관계의 주인에 값을 입력하지 않음

이 경우를 가장 조심해야 한다.
연관관계의 주인에 값을 입력하지 않으면 아래와 같이 아무것도 들어오지 않는다. (읽기 전용에 값을 아무리 입력해봤자임)

insert query 2번db

양방향 매핑시 양쪽 다 입력하도록 하자

양방향 매핑시 연관관계의 주인에 값을 입력해야 한다.
순수한 객체 관계를 고려하면 가장 아래와 같이 항상 양쪽 다 값을 입력해주도록 하자.

순서 바꿔서

추가 설명
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);

위 코드들을 모~두 쓰는 것은 헷갈리니까 하나만
→ 즉, 팀에서 멤버를 넣을건지 멤버에서 팀을 넣을건지를 결정해야 함.

  • 양방향 매핑시 무한 루프 조심
    (lombok에서 toString() 만드는걸 자제해라)
    (controller에서는 entity를 절대 반환하지 마라)

정리

  • 단방향 매핑만으로도 이미 연관관계 매핑은 완료
    (처음에 설계할 때 단방향 매핑으로 설계를 끝내야 함)
    객체 입장에서 가급적 단방향 매핑이 좋은 것. 양방향 매핑은 신경써야 할 것이 많아 복잡도가 커짐.

  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐

  • 그럼 양방향 매핑은 왜 쓰냐?
    JPQL에서 역방향으로 탐색할 일이 많음

  • 단방향 매핑을 잘 해두면 양방향은 필요할 때 추가해도 됨(테이블에 영향을 주지 않기 때문)

⭐️ 연관관계의 주인은 외래 키의 위치를 기준으로 정해야 함

비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안된다.
반드시 ⭐️ 연관관계의 주인은 외래 키의 위치를 기준으로 정하자.


[4] 실전 예제 2 - 연관관계 매핑 시작

실습은 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<>();

profile
개발 바보 이사 중
post-custom-banner

0개의 댓글