[JPA/김영한] 여러가지 연관관계 매핑을 알아보자

수영·2021년 9월 29일
2

JPA 공부!

목록 보기
5/9
post-custom-banner

이 글은 김영한님의 JPA 강의 중 6장을 듣고 정리한 내용입니다 :)
강의 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
교재 : 자바 ORM 표준 JPA 프로그래밍🤷‍♀️

💡 연관관계 매핑 할때, 3가지를 고려하자-!

1. 다중성 결정하기

  • 다대일 (주인쪽에 걸어주는 것!)
  • 일대다 (일쪽에서 다를 조회하는 로직이 필요하다면 매핑 걸어줄것!)
  • 일대일
  • 다대다 (실무에서는 쓰지 말것)

2. 단방향, 양방향 정하기

  • 테이블의 경우 : '방향'이라는 개념 없음, 그냥 외래키로 조인하면 두 테이블이 연관관계를 맺음
  • 객체의 경우 : 연관관계 매핑을 객체 참조하는 방식으로 하여,참조가 2군데 있어야함 ( A->B, B->A )
    ⇒ 외래키를 뭐로 할지, 누가 외래키를 관리할 지 정해줘야 함
    ⇒ 연관관계의 주인을 만들어야함!

3. 연관관계의 주인 정해주기

: 양방향의 경우, 누가 주인(= 외래키 관리하는 쪽)인지 정해주자


다중성 4가지 종류를 알아보자!
여기서는 다대일 양방향 매핑이 중요하고, 다대다는 실무에서 안 쓴다!

일대다 @OneToMany

  • 일대다 단방향 [1:N] : '일'이 연관관계의 주인임, @JoinColumn을 넣어주어야함!
  • 일대다 양방향 [1:N, N:1] : 이보다는 다대일 양방향('다'쪽이 주인)을 추천한다.

만약 '일'쪽에서 외래키를 관리(주인)하겠다면 헷갈리기 때문에 비추함!
⇒ 다대일 관계에서 주인은 항상 '다' 쪽에 있으므로,
다대일 양방향을 추천한다. '다'쪽에서 '일'쪽을 조회할 일이 없더라도, '다'를 연관관계의 주인으로 설정하자!

다만, 공식적이지는 않으나 일대다 양방향을 하고 싶으면,
@JoinColumn (insertable=false, updatable=false) 이걸 '다'쪽에 걸어주면 읽기만 할 수 있음

아래는 일대다 단방향으로, 일대다 양방향은 다대일 양방향으로 가자!

@Entity
@Getter @Setter
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private String id;

    private String name;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();
}

@Entity
@Setter
@Getter
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private String id;

    private String username;
}

다대일 @ManyToOne

  • 다대일 단방향 [N:1] : 주인인 '다'쪽에서는 '일'쪽 참조 가능하나, 반대는 조회도 불가능!
  • 다대일 양방향 [N:1, 1:N] : 서로 참조하면 주인아닌 쪽에서는 조회 가능해짐, 연관관계 편의 메소드를 만들어주면 좋음 (무한 루프 주의!)

아래는 다대일 단방향, 양방향 코드이다

@Entity
@NoArgsConstructor
@ToString(exclude = "team")
@Getter @Setter
public class Member {
    @Id
    @Column(name = "MEMBER_ID")
    private String id;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID") // 다 쪽에서 걸어주기
    private Team team;
}

@Entity
@NoArgsConstructor
@Getter @Setter
public class Team {
    @Id
    @Column(name = "TEAM_ID")
    private String id;

		// 이걸 해주면 양방향 성립
		@OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
}

일대일 @OneToOne

: 주 테이블이나 대상 테이블, 둘 중 어디에나 외래키 넣어도 OK!
: 일대일 양방향일때는 주인이 아닌쪽에 mappedBy 를 걸어주면 된다.

  • 주 테이블에 외래키 : 객체지향 개발자들이 선호하는 방식으로, 주 객체가 대상 객체의 참조를 가지는 것 처럼 주 테이블에 외래 키를 두고 대상 테이블을 찾는다.
  • 대상 테이블에 외래키 : DB쪽 개발자들이 선호하는 방식으로, 일대일에서 일대다로 변경 시 테이블 구조를 그대로 유지할 수 있다. 그러나, 지연로딩이 안 되고 즉시로딩이 된다. (나중에 자세히-!)

아래는 일대일 단방향, 양방향 코드이다

@Entity
@Getter
@Setter
public class User {
    @Id @GeneratedValue
    @Column(name = "user_id")
    private String id;

    @OneToOne
    @JoinColumn(name = "locker_id")
    private Locker locker;   
}

@Entity
@Getter @Setter
public class Locker {
    @Id @GeneratedValue
    @Column(name = "locker_id")
    private Long id;

		// (이 파트가 없으면 단방향)
    @OneToOne(mappedBy = "locker") // 이걸 걸어주고 
    private Member member;         // 객체를 참조해주면 양방향임                           
}

다대다 @ManyToMany

  • 객체의 경우 : 다대다 관계가 가능함
  • 테이블의 경우 : 기존 테이블 두개만으로 다대다 관계를 만들수 없다.
    대신에 보통은 연결 테이블을 추가하여, 일대다, 다대일로 풀어내자.
    @ManyToMany → @OneToMany, @ManyToOne 이렇게 풀어내어 만들자.

다대다 관계를 매핑할때 @ManyToMany는 사용하지 않는 걸 권장하는데, 아래 그림을 참고해보자

여기서 첫번째 그림처럼 @ManyToMany사용 + 연결 테이블 생성만 하면 실무에서 복잡해진다 (연결 테이블에 다른 컬럼값들을 추가하면, 더 이상 다대다 관계로 매핑하지 못해서..???)

그렇기에 연결 테이블을 엔티티로 승격시켜주어, 다른 컬럼 값들도 추가 가능하게 하자!

따라서 두번째 그림처럼
@ManyToMany → @OneToMany, @ManyToOne 이렇게 풀어내고 + 연결 테이블용 엔티티 추가하자!

아래는 비추하는 다대일 매핑

@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private String id;
    
    @ManyToMany
    @JoinTable(name = "MEMBER_PRODUCT", //연결테이블 지정
								joinColumns = @JoinColumn(name = "MEMBER_ID"), 
								inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
    List<Product> products = new ArrayList<Product>();
}

@Entity
public class Product {
    @Id
    @JoinColumn(name = "PRODUCT_ID")
    private String id;
}

아래는 추천하는 @ManyToMany → @OneToMany, @ManyToOne 이렇게 풀어내고 + 연결 테이블용 엔티티(Order) 추가 코드

@Entity
public class Member {
  @Id @Column(name = "MEMBER_ID")
  private String id;

  @OneToMany(mappedBy = "product")
  private List<Order> orders;
}

@Entity // 연결 테이블용 엔티티(Order)
public class Order {
  @Id
  @ManyToOne
  @JoinColumn(name = "MEMBER_ID")
  private Member member;

  @Id
  @ManyToOne
  @JoinColumn(name = "PRODUCT_ID")
  private Product product;
}

@Entity
public class Product {
  @Id @Column(name = "PRODUCT_ID")
  private String id;

  @OneToMany(mappedBy = "product")
	private List<Member> members;
}

참고 래퍼런스
https://sa1341.github.io/2019/10/01/다양한-연관관계-매핑/
https://incheol-jung.gitbook.io/docs/study/jpa/6

profile
🎵🎵🎵🎶🎵
post-custom-banner

0개의 댓글