북스터디 : 스프링 부트 핵심가이드(5)

윤장원·2023년 4월 9일
0

9. 연관관계 매핑

연관관계 매핑 종류와 방향

연관관계의 종류

  • One To One : 일대일(1:1)
  • One To Many : 일대다(1:N)
  • Many To One : 다대일(N:1)
  • Many To Many : 다대다(N:M)

연관관계 방향

  • 단방향 : 두 엔티티의 관계에서 한쪽의 엔티티만 참조하는 형식
  • 양방향 : 두 엔티티의 관계에서 각 엔티티가 서로의 엔티티를 참조하는 형식

일대일 매핑

일대일 단방향 매핑

@OneToOne 어노테이션은 다른 엔티티 객체를 필드로 정의했을 때 일대일 연관관계로 매핑하기 위해 사용한다. 뒤이어 @JoinColumn 어노테이션을 사용해 매핑할 외래키를 설정한다. @JoinColumn 어노테이션은 기본값이 설정돼 있어 자동으로 이름을 매핑하지만 의도한 이름이 들어가지 않기 때문에 name 속성을 사용해 원하는 칼럼명을 지정하는 것이 좋다. 만약 @JoinColumn을 선언하지 않으면 엔티티를 매핑하는 중간 테이블이 생기면서 관리 포인트가 늘어나 좋지 않다.

ProductDetail 엔티티

@OneToOne
@JoinColumn(name = "product_number")
private Product product;

일대일 양방향 매핑

Product 엔티티

@OneToOne
private ProductDetail productDetail;

이렇게 하면 양쪽에서 외래키를 가지고 left outer join이 두 번이나 수행되기 때문에 효율성이 떨어진다. 실제 데이터베이스에서도 테이블 간 연관관계를 맺으면 한쪽 테이블이 외래키를 가지는 구조로 이뤄진다. JPA에서도 실제 데이터베이스의 연관관계를 반영해서 한쪽의 테이블에서만 외래키를 바꿀 수 있도록 정하는 것이 좋다. 이 경우 엔티티는 양방향으로 매핑하되 한쪽에게만 외래키를 줘야 하는데, 이때 사용되는 속성 값이 mappedBy이다.

mappedBy 속성을 추가한 Product 엔티티

@OneToOne(mappedBy = "product")
private ProductDetail productDetail;

mappedBy에 들어가는 값은 연관관계를 갖고 있는 상대 엔티티에 있는 연관관계 필드의 이름이 된다.

다대일, 일대다 매핑

다대일 단방향 매핑

Provider 엔티티

@Id
public class Provder extends BaseEntity {
	
    @Id
    @GeneratedValue(startegy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
}

Product 엔티티

@ManyToOne
@JoinColumn(name = "provider_id")
@ToString.Exclude
private Provider provider;

다대일 양방향 매핑

Provider 엔티티

@OneToMany(mappedBy = "provider", fetch = FetchType.EAGER)
@ToString.Exclude
private List<Product> productList = new ArrayList<>();

일대다 연관관계의 경우 여러 상품 엔티티가 포함될 수 있어 컬렉션(Collection, List, Map) 형식으로 필드를 생성한다. 이렇게 @OneToMany가 붙은 쪽에서 @JoinColumn 어노테이션을 사용하면 상대 엔티티에 외래키가 설전된다.

일대다 단방향 매핑

Category 엔티티

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(unique = true)
private String code;

private String name;

@OnetoMany(fetch = FetchType.EAGER)
@JoinColumn(name = "category_id")
private List<Product> products = new ArrayList<>();

@OneToMany와 @JoinColumn을 사용하면 Product 엔티티에서 별도의 설정을 하지 않아도 일대다 단방향 연관관계가 매핑된다. @JoinColumn은 필수 사항은 아니지만 사용하지 않으면 중간 테이블로 Join 테이블이 생성되는 전략이 채택된다.

다대다 매핑

다대다 연관관계에서는 각 엔티티에서 서로를 리스트로 가지는 구조가 만들어진다. 이런 경우에는 교차 엔티티라고 부르는 중간 테이블을 생성해서 다대다 관계를 일대다 또는 다대일 관계로 해소한다.

다대다 단방향 매핑

Producer 엔티티

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String code;

private String name;

@ManyToMany
@ToString.Exclude
private List<Product> products = new ArrayList<>();

public void addProduct(Product product) {
	products.add(product);
}

다대다 연관관계는 @ManyToMany 어노테이션으로 설정한다. 리스트로 필드를 가지는 객체엣는 외래키를 가지지 않기 때문에 별도의 @JoinColumn은 설정하지 않아도 된다.

다대다 양방향 매핑

Product 엔티티

@ManyTOMany
@ToString.Exclude
private List<Producer> producers = new ArrayList<>();

public void addProducer(Producer producer){
	this.producers.add(producer);
}

다대다 연관관계에서는 중간 테이블이 생성되기 때문에 예기치 못한 쿼리가 생길 수 있다. 즉, 관리하기 힘든 포인트가 발생한다는 문제가 있다. 그렇기 때문에 이러한 다대다 연관관계의 한계를 극복하기 위해서는 중간 테이블을 생성하는 대신 일대다 다대일로 연관관계를 맺을 수 있는 중간 엔티티로 승격시켜 JPA에서 관리할 수 있게 생성하는 것이 좋다.

영속성 전이

영속성 전이(cascade)란 특정 엔티티의 영속성 상태를 변경할 때 그 엔티티와 연관된 엔티티의 영속성에도 영향을 미쳐 영속성 상태를 변경하는 것을 의미한다. 연관관계와 관련된 어노테이션을 보면 cascade()라는 요소를 볼 수 있다. 이 어노테이션은 영속성 전이를 설정하는 데 활용된다.

영속성 전이 타입의 종류

  • ALL : 모든 영속 상태 변경에 대해 영속성 전이를 적용
  • PERSIST : 엔티티가 영속화할 때 연관된 엔티티도 함께 영속화
  • MERGE : 엔티티를 영속성 컨텍스트에 병합할 때 연관된 엔티티도 병합
  • REMOVE : 엔티티를 제거할 때 연관된 엔티티도 제거
  • REFRESH : 엔티티를 새로고침할 때 연관된 엔티티도 새로고침
  • DETACH : 엔티티를 영속성 컨텍스트에서 제외하면 연관된 엔티티도 제외

영속성 전이에 사용되는 타입은 엔티티 생명주기와 연관이 있다.

고아 객체

JPA에서 고아(orphan)란 부모 엔티티와 연관관계가 끊어진 엔티티를 의미한다. JPA에는 이러한 고아 객체를 자동으로 제거하는 기능이 있다. 물론 자식 엔티티가 다른 엔티티와 연관관계를 가지고 있다면 이 기능은 사용하지 않는 것이 좋다.

연관관계와 관련된 어노테이션에 'orphanRemoval = true' 속성을 넣게 되면 고아 객체를 제거할 수 있다. 연관관계가 제거되면서 상태 감지를 통해 삭제하는 쿼리가 수행된다.

0개의 댓글