JPA 연관 관계-2021.12.05

Jonguk Kim·2021년 12월 5일
0

Spring 개념

목록 보기
1/2

1. JPA에서 중요한 것

객체와 관계형 데이터베이스 테이블을 어떻게 매핑할지 이다.
(목적: 객체 지향 프로그래밍과 데이터베이스 사이의 패러다임 불일치를 해결하는 것)

1-1. 방향

  • 단방향 : 회원, 팀 관계에서 회원 -> 팀 또는 팀 -> 회원 한 쪽으로만 참조한다면 단방향
  • 양방향 : 회원 -> 팀, 팀 -> 회원 양쪽에서 서로를 참조하고 있을 때 양방향
    (서로를 참조하는 양방향 관계는 객체에서만 존재하고, 테이블은 항상 양방향이다!)
    • DB 테이블은 외래 키 하나로 양 쪽 테이블 조인(양방향 관계 : <->)이 가능
    • 객체에서는 참조용 필드가 있어야 다른 객체를 참조할 수 있기 때문에 사실상 객체에서의 양방향 관계는 <-> 이런 관계가 아니라
      단방향 참조를 각각 가진 <- , -> 이러한 관계

1.1.1. DB 테이블에서의 양방향 예시

DB 테이블은 외래 키 하나로 양 쪽 테이블 조인이 가능하다. (다리 역할)
아래 그림 예시를 보면 TEAM과 MEMBER는 1대 다 관계를 가지고 있고 MEMBER에서 TEAM의 ID를 외래키로 가지고 있다.

// 내가 속한 팀 찾기
SELECT *
FROM TEAM A INNER JOIN MEMBER B
ON A.ID = B.TEMA_ID

// 팀에 속한 멤버 찾기
SELECT *
FROM MEMBER A INNER JOIN TEAM B
ON A.TEAM_ID = B.ID
sql

1.1.2. 객체에서의 단방향, 양방향 예시

  • Member -> Team 단방향 연관 관계
    • 참조용 필드가 있는 객체만 다른 객체를 참조하는 것이 가능
    • Member 클래스에서 Team 클래스 참조 가능
    • Team 클래스에서 Member 클래스 참조 불가능 (Team에서는 Member의 참조용 필드가 없기 때문)
class Member{
        Team tema1;
}

class Team {}
  • Member <- -> Team 양방향 연관 관계
    • 서로 참조 가능
    • Member -> Team 단항향, Team -> Member 단방향
class Member{
        Team tema1;
}

class Team {
        Member member1;
}

1-2. 연관 관계의 주인

객체를 양방향 연관 관계로 만들면 연관 관계의 주인을 지정해야 한다.

  • 객체 연관관계

    • 회원 → 팀 연관관계 1개(단방향)
    • 팀 → 회원 연관관계 1개(단방향)
    • 참조가 2개, 외래키는 어디에 ?
  • 테이블 연관관계

    • 회원 <-> 팀 연관관계 1개(양방향)
    • 회원 테이블에 외래 키 하나
  • 객체 연관 관계를 단방향으로 매핑했을 때는 참조를 하나만 사용하기 때문에 연관 관계 주인 필요 없다.

  • 하지만 양방향 관계로 매핑해버리면 참조가 2곳이 된다. (JPA는 혼란 - 패러다임 불일치)

  • JPA에서는 두 객체 연관 관계 중 하나를 정해 테이블의 외래키를 관리해야 한다. (연관 관계의 주인)

  • 연관관계 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있다.
    주인이 아닌 쪽은 읽기만 할 수 있다.

=> 연관 관계의 주인은 외래 키가 있는 쪽으로 설정
=> Member 엔티티에서 Team_id를 외래키로 가지고 있기 때문에 Member를 주인으로 설정해야 한다!

1-3. 다중성 (연관 관계 종류)

데이터베이스를 기준으로 다중성을 결정한다.

1-3-1. 다대일 (@ManyToOne)

  • Member 하나는 하나의 Team을 가진다.
  • 하나의 Team에는 여러 Member가 있을 수 있다.
  • Member 다 : 일 Team
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "MEMBER_NAME", nullable = false)
    private String NAME;

    @ManyToOne // 멤버 N : 팀 1
    @JoinColumn(name = "TEAM_ID")
    private Team team;	// Team 자체를 참조
}

1-3-2. 일대다 (@OneToMany)

  • 다대일 예제와 같지만 일대다의 차이는 참조를 1쪽에서하는 것 (외래키 관리는 1쪽에서 함)
  • 보통 다대일 단방향에서 외래키를 관리하는 것이 보통이며 실무에서는 일대다 단방향 연관 관계는 잘 안씀
  • 일대다 단방향 연관 관계 매핑이 필요한 경우는 그냥 다대일 연관 관계를 매핑해버리는게 유지보수에 편함
    • Team 엔티티 안에 있는 Member의 정보를 수정한다면 Member 테이블에 FK가 없기 때문에 조인 + 업데이트 쿼리를 날려 수정해야 함
@Entity
public class Member {
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "MEMBER_NAME", nullable = false)
    private String NAME;
}

@Entity
public class Team {
    @Id @GeneratedValue
    private Long id;

    @Column(name = "TEAM_NAME", nullable = false)
    private String name;

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

1-3-3. 일대일 (@OneToOne)

말그대로 하나랑 하나가 매칭되는 것이므로 어느 쪽에 외래키를 설정하든 문제는 없다.

1-3-4. 다대다 (@ManyToMany)

실무에서 보통 사용 금지라고 한다.

  • 중간 테이블이 숨겨져 있어 개발자도 모르는 복잡한 조인 쿼리가 발생할 경우가 생길 수 있다.
  • 다대다로 자동 생성된 중간 테이블은 두 객체의 테이블 외래키만 저장되기 때문에 문제가 될 확률이 높다
  • 일대다, 다대일로 풀어서(중간 테이블을 엔티티로 만드는 것) 만드는 것이 추후 유지보수에도 좋다.
profile
Just Do It

0개의 댓글