Back-end/JPA

[JPA] 연관관계 매핑

sw_develop 2022. 4. 14. 09:59

# 연관관계가 필요한 이유

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

- 테이블은 외래 키(FK)로 조인을 사용해 연관된 테이블을 찾는다.

- 객체는 참조를 사용해 연관된 객체를 찾는다.

 

# 연관관계 매핑시 고려사항 3가지

  • 단방향 vs. 양방향
  • 양방향 관계일 때의 연관관계의 주인
  • 다중성
    • @ManyToOne, @OneToMany, @OneToOne, @ManyToMany

 

1. 단방향 vs. 양방향

테이블

  • 외래 키 하나로 양쪽 조인 가능
  • 사실 방향이라는 개념이 없음

객체

  • 참조용 필드가 있는 쪽으로만 참조 가능
  • 한쪽만 참조하면 단방향
  • 양쪽이 서로 참조하면 양방향

 

# 단방향 연관관계

객체 지향 모델링 - ORM 매핑

객체 지향 모델링 - 객체의 참조와 테이블의 외래 키를 매핑

@Entity
public class Member {
	
    @Id @GeneratedValue
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
    
    ...
}

객체 지향 모델링 - 연관관계 저장, 조회(객체 그래프 탐색), 수정

//저장
Team team = new Team();
team.setName("TeamA");
entityManager.persist(team);

Member member = new Member();
member.setName("member1");
member.setTeam(team);	//단방향 연관관계 설정, 참조 저장
entityManager.persist(member);

//조회
Member findMember = entityManager.find(Member.class, member.getId());

Team findTeam = findMember.getTeam();	//참조를 사용해 연관관계 조회

//수정
Team teamB = new Team();
entityManager.persist(teamB);

member.setTeam(teamB);	//새로운 참조 설정

 

# 양방향 연관관계

양방향 매핑

  • DB 테이블에는 변화가 없고, 엔티티에서만 List 컬렉션이 추가된다.
  • 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개이다.
    • 단방향 매핑만으로도 이미 연관관계 매핑은 완료된 것이다.
    • 양방향은 필요할 때 추가해도 된다. (어차피 테이블에 영향을 주지 않으니까)
    • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐이다.
  • 테이블은 외래 키 하나로 양방향 연관관계(양쪽으로 조인 가능)를 가지고 두 테이블의 연관관계를 관리한다.

 

2. 양방향 관계일 때의 연관관계의 주인

  • 양방향 관계 중 하나를 연관관계의 주인으로 지정해야 한다.
  • 연관관계의 주인만이 외래 키를 관리한다. (DB 테이블에 외래 키 등록 및 수정)
    • 연관관계의 주인 쪽에 값을 입력하지 않으면, DB 테이블의 FK 값이 null이 된다.
  • 주인이 아닌 쪽은 읽기만 가능하고, mappedBy 속성으로 주인을 지정해야 한다.
  • 외래 키가 있는 곳을 주인으로 정해라! (아래 예시에서는 Member.team이 연관관계의 주인이 됨)

 

양방향 매핑 예시

  • Member.team이 연관관계의 주인일 때
    • Member 엔티티 코드는 위와 동일하고, Team 엔티티에만 컬렉션이 추가된다.
@Entity
public class Team {
	
    @Id @GeneratedValue
    private Long id;
    
    @OneToMany(mappedBy = "team")
    List<Member> members = new ArrayList<>();
    
    ...
}

 

3. 다중성

# 다대일(N:1) - 연관관계의 주인이 N인 쪽일 때

다대일 단방향
다대일 양방향

→ 외래 키가 있는 쪽이 연관관계의 주인

→ 양쪽을 서로 참조하도록 설정

 

 

# 일대다(1:N) - 연관관계의 주인이 1인 쪽일 때

일대다 단방향

→ 해당 경우는 일대다(1:N)에서 1이 연관관계의 주인인 경우이다.

→ DB 테이블의 일대다 관계에서는 항상 N 쪽에 외래 키가 존재한다. 

→ 권장하지 않고, 다대일 양방향 관계를 사용하자

일대다 양방향

→ 위와 같은 매핑은 공식적으로 존재하지 않는다.

→ 읽기 전용 필드를 사용해 양방향처럼 사용하는 방법이다.

→ 권장하지 않고, 다대일 양방향 관계를 사용하자

 

# 일대일(1:1)

매핑방식1 - DB 주 테이블에 외래 키

주 테이블에 외래 키 단방향

→ @ManyToOne 단방향 매핑과 유사

주 테이블에 외래 키 양방향

→ 다대일 양방향 매핑처럼 테이블에 외래 키가 있는 곳이 연관관계의 주인이 된다.

→ 반대편은 mappedBy 속성으로 주인 설정

 

매핑방식2 - DB 대상 테이블에 외래 키

→ 대상 테이블에 외래 키가 존재한다.

→ 장점: 주 테이블과 대상 테이블을 1:1에서 1:N 관계로 변경할 때 테이블 구조 유지 가능

→ 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨 

 

# 다대다(M:N)

매핑방식1 - @ManyToMany로 처리

→ @JoinTable로 중개 테이블을 지정한다. 

→ 실무에서는 권장하지 않는다. (중개 테이블이 FK 이외의 필드를 가질 확률이 굉장히 높기 때문에)

 

매핑방식2 - 연결 테이블용 엔티티 추가 (주로 이것 사용!)

→ @ManyToMany를 @OneToMany와 @ManyToOne으로 나누어 처리한다.