Back-End/JPA

6. 다양한 연관관계 매핑

wisdom11 2022. 4. 30. 23:33

 

1. 다대일

N:1에서 외래키는 항상 N쪽에 있다.
따라서 N:1 양방향 관계에서 연관관계의 주인은 N쪽이다.

1.1. 다대일 단방향 (N:1)

한쪽만 참조를 하고, 반대편은 참조하지 않는다(참조하는 필드가 없다).

다(N)쪽 테이블 매핑 엔티티에 참조 필드 추가
  • @ManyToOne
  • @JoinColumn(name = 외래키이름)

 

1.2. 다대일 양방향 (N:1, 1:N)

외래키가 있는 쪽이 연관관계의 주인이다.
  • 주인이 아닌 쪽은 항상 연관관계에 있는 엔티티를 조회만 해야한다.

 

항상 서로를 참조한다.
  • 항상 서로를 참조하도록 하려면 연관관계 편의 메소드를 작성하는 것이 좋고,
    무한루프에 빠지지 않도록 검사하는 로직을 넣어주는 것이 좋다.
// 회원 엔티티 내부
public void setTeam(Team team) {
	this.team = team;
	
	// 무한루프에 빠지지 않도록 체크
	if(!team.getMembers().contains(this)) {
		team.getMembers().add(this);
	}
}

// 팀 엔티티 내부
public void addMember(Member member) {
	this.members.add(member);

	// 무한루프에 빠지지 않도록 체크
	if(member.getTeam() != this) {
		member.setTeam(this);
	}
}
 
다(N)쪽 테이블 매핑 엔티티에 참조 필드 추가
  • 단방향과 동일
 
일(1)쪽 테이블 매핑 엔티티에 참조 필드 추가
  • @OneToMany (mappedBy = 반대쪽 테이블 매핑 필드 이름)

 

 

2. 일대다

다대일의 반대 방향.
하나 이상의 엔티티를 참조할 수 있다.
→ 자바 컬랙션 Collection, List, Set, Map 중에 하나를 사용해야 한다.

2.1. 일대다 단방향 (1:N)

일대다 단방향 관계는 JPA 2.0부터 지원한다.
반대쪽 테이블에 있는 외래키를 관리한다.

일(1)쪽 테이블 매핑 엔티티에 참조 필드 추가
  • @OneToMany
  • @JoinColumn(name = 반대쪽 테이블의 외래키 이름)

 

단점
  • 외래키가 다른 테이블에 있다. (다른 테이블의 외래키를 관리해야 한다)
    → 외래키를 등록/수정/삭제 시 sql을 추가로 실행해야 한다. (성능 문제)
    → 관리가 부담스럽다.
    ⇒ 따라서, 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자

 

2.2. 일대다 양방향 (1:N, N:1)

일대다 양방향 매핑은 존재하지 않는다.
→ 다대일 양방향 매핑을 사용해야 한다.

완전히 불가능한 것은 아니다.
→ 반대편에 같은 외래 키를 사용하는 다대일 단방향 매핑을 읽기 전용(insertable=false, updatable=false)으로 하나 추가하면 된다.
→ 읽기 전용으로 하지 않으면 둘 다 같은 키를 관리하므로 문제가 발생할 수 있다.

 

 

3. 일대일

주 테이블이나 대상 테이블 둘 중 어느 테이블이나 외래키를 가질 수 있다.

주 테이블에 외래키
  • 객체지향 개발자들이 선호
  • 주 테이블만 확인해도 대상 테이블과 연관관계가 있는지 알 수 있다.
 
대상 테이블에 외래키
  • 전통적인 데이터베이스 개발자들이 선호
  • 일대일에서 일대다로 관계를 변경할 때 테이블 구조를 그대로 유지할 수 있다.

 

3.1. 주 테이블에 외래키 - 단방향

주 테이블 매핑 엔티티에 참조 필드를 추가
  • @OneToMany
  • @JoinColumn(name = 외래키이름)

 

3.2. 주 테이블에 외래키 - 양방향

주 테이블 매핑 엔티티에 참조 필드를 추가
  • 단방향과 동일
 
대상 테이블 매핑 엔티티에 참조 필드 추가
  • @OneToOne(mappedBy = 주테이블 매핑 필드 이름)

 

3.3. 대상 테이블에 외래키 - 단방향

JPA에서 지원하지 않는다.

 

3.4. 대상 테이블에 외래키 - 양방향

일대일 매핑에서 대상 테이블에 외래키를 두고 싶을 경우 양방향으로 매핑해야 한다.

주 테이블 매핑 엔티티에 참조 필드를 추가
  • @OneToOne(mappedBy = 대상 테이블 매핑 필드 이름)
 
대상 테이블 매핑 엔티티에 참조 필드 추가
  • @OneToOne
  • @JoinColumn(name = 외래키이름)

 

 

4. 다대다

주 테이블이나 대상 테이블 둘 중 어느 테이블이나 외래키를 가질 수 있다.

관계형 데이터베이스
  • 테이블 2개로 다대다 관계를 표현할 수 없다.
  • 보통 다대다 관계를 일대다, 다대일 관계로 풀어내는 연결 테이블을 사용한다.
 
객체
  • 객체 2개로 다대다 관계를 만들 수 있다.

 

4.1. 다대다 단방향

엔티티에 참조 필드 추가
  • @ManyToMany
  • @JoinTable(name = 연결 테이블 이름, joinColumns = @JoinColumn(name=현재 방향 조인 컬럼 이름),
    inverseJoinColumns = @JoinColumn(name=반대 방향 조인 컬럼 이름))

⇒ 연결 테이블을 바로 매핑 (연결 엔티티 사용하지 않음)

 

@JoinTable

속성 기능
name 연결 테이블을 지정한다.
joinColumns 현재 방향 테이블과 매핑할 조인 컬럼 정보를 지정한다.
inverseJoinColumns 반대 방향 테이블과 매핑할 조인 컬럼 정보를 지정한다.

 

4.2. 다대다 양방향

연관관계의 주인 엔티티에 참조 필드 추가
  • 단방향과 동일
 
연관관계의 주인이 아닌 엔티티에 참조 필드 추가
  • @ManyToMany(mappedBy = 외래키이름)

 

4.3. 다대다 매핑의 한계

연결 테이블에 외래키 외의 추가적인 컬럼이 있는 경우, 이 컬럼을 사용할 수 없다.
예를 들어 Member-Product 의 연결 테이블에 주문수량, 주문날짜 컬럼이 있을 경우, 이 컬럼에 접근할 수 없다.
결국 연결 테이블을 매핑하는 연결 엔티티를 만들고 이곳에 해당 컬럼들을 매핑해야 한다.

 

4.4. 다대다 - 연결 엔티티 사용

연결 엔티티가 외래키를 가지고 있으므로 연관관계의 주인이 된다.

연결 엔티티에 참조 필드 2개 추가
  • @ManyToOne + @JoinColumn(name = 외래키1 이름)
  • @ManyToOne + @JoinColumn(name = 외래키2 이름)

 

연결 엔티티의 기본 키는 어떻게 처리할까? (2가지 방법)

1️⃣ 외래키 조합의 복합 기본키 사용 → 7.3절

  • 식별자 클래스 생성
    • public 이어야 한다.
    • 기본 생성자가 있어야 한다.
    • Serializable을 구현해야 한다.
    • equals와 hashCode 메소드를 구현해야 한다.
    public class MemberProductId implements Serializable {
          private String member;
          private String product;
    
          @Override
          public boolean equals(Object o) { ... }
    
          @Override
          public int hashCode() { ... }
      }
      
  • 복합 키를 위한 식별자 클래스 특징

 

  • @IdClass 로 식별자 클래스 지정
@IdClass(MemberProductId.class)
@Entity
public class MemberProduct {

	@Id
	@ManyToOne
	@JoinColumn(name = "MEMBER_ID")
	private Member member;

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

	...
}

 

  • Identifying Relationship (식별 관계)
    부모 테이블의 기본키를 받아서 자신의 기본키와 외래키로 사용하는 것을 지칭하는 데이터베이스 용어

 

복합키를 사용하는 방법
⇒ 처리할 일이 상당히 많다.

 

2️⃣ 새로운 기본키 사용

간단히 다대다 관계를 구성할 수 있다. (아래 참고)

 

4.5. 다대다 - 새로운 기본 키 사용

MemberProduct → Order 로 이름 변경

@Entity
public class Order {

	@Id @GeneratedValue
	@Column(name = "ORDER_ID")
	private Long Id;
	
	@ManyToOne
	@JoinColumn(name = "MEMBER_ID")
	private Member member;

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

	...
}

⇒ 식별자 클래스를 사용하지 않아서 코드가 단순해졌다.

 

4.6. 다대다 연관관계 정리

식별 관계: 받아온 식별자를 기본키와 외래키로 사용한다.
비식별 관계: 받아온 식별자를 외래키로만 사용하고, 새로운 식별자를 추가한다.
⇒ 객체 입장에서 비식별 관계를 사용하는 것이 복합 키를 위한 식별자 클래스를 만들지 않아도 되므로 단순하고 편리하게 ORM 매핑을 할 수 있다.

 

 

 

728x90