[스프링 부트 핵심 가이드] 정리 5주차

AmeriKano·2023년 6월 16일
0

책 링크

매주 스프링 부트 핵심 가이드 를 읽으며 이전에 궁금했거나, 새롭게 알게 된 내용들을 정리할 예정이다.
(이번 주는 9장을 읽었다.)


연관관계 매핑

두 엔티티 간의 연관관계는 다음과 같다.

  • One to One: 일대일
  • One to Many: 일대다
  • Many to One: 다대일
  • Many to Many: 다대다

어떤 엔티티를 중심으로 연관된 엔티티를 보느냐에 따라 연관관계가 달라진다. (예를 들어, 상품과 그 상품을 생산하는 업체가 있다 했을 때, 업체 입장에서는 한 업체가 여러 개의 상품을 가지므로 일대다, 반대로 상품은 여러 종류의 상품이 한 업체에서 만들어지므로 다대일이라고 할 수 있다.)
일반적으로 데이터베이스에서는 연관관계를 설정하면 외래 키를 통해 테이블을 조인하는 방식으로 생성되나 JPA에서는 엔티티 사이의 참조 방향을 설정할 수 있다. 이는 양방향으로 설정해도 무관하나 단방향으로만 설정해도 해결되는 경우가 많다. 일반적으로 외래키를 가지는 테이블이 관계의 주인이 되며, 주인은 외래키를 사용할 수 있으나 상대는 읽기만 수행할 수 있다.

일대일 매핑

연관관계를 가지는 다른 엔티티 객체에 어노태이션을 달아준다.

// 자동으로 이름을 매핑하지만 의도한 컬럼이 들어가지 않을 수도 있으므로
// @JoinColumn 을 사용하여 이름을 지정해준다.
// @OneToOne optional : false인 경우 null을 허용하지 않는다.
@OneToOne(optional = false)
@JoinColumn(name = "column_name")
private Product product;

다른 연관관계의 경우에도 그렇지만, 실행하여 생성되는 SQL을 확인해보면 자동으로 테이블 조인이 수행되는 것을 확인할 수 있다.
(위의 optional이 기본값 true인 경우 조인은 outer join 이 실행되고, false인 경우 inner join이 실행된다. 객체의 설정에 따라 JPA는 최적의 쿼리를 생성한다는 것을 알 수 있다.)

양방향 매핑은 각각 객체에 @OneToOne 을 달아주면 되는데, 이 경우 주인을 명시하지 않으면 조인이 두 번 실행돼 속도 면에서 손해가 발생할 수 있다. 그러므로 mappedBy 설정 값을 추가하여 주인을 표시한다.

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

위와 같이 작성하면, ProductDetail 엔티티가 Product 엔티티의 주인이 된다. 이렇게 양방향으로 관계를 설정하면 toString 을 실행할 때 순환참조가 발생하여 에러가 발생한다. 그러므로 필요한 경우가 아니라면 단방향으로 연관관계를 설정하거나 양방향이 필요하다면 위처럼 제외 설정하는 것이 좋다.

일대다, 다대일 매핑

일대일 매핑과 유사하게, @ManyToOne 또는 @OneToMany 를 객체에 달아주면 된다. 다만 주의할 점은 일대다의 경우 여러 상대 엔티티가 포함될 수 있으므로 컬렉션의의 형식으로(대표적인 컬렉션으로 List, Map 이 있다.) 선언한다.

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

위에서 처럼 fetch 타입을 설정해주는 이유는 일대다의 기본 fetch 전략이 지연 로딩(Lazy) 이기 때문이다.

지연로딩(Lazy loading)과 즉시로딩(Eager loading) 은 중요한 개념이다. 엔티티 객체의 개념으로 데이터베이스를 구현했으므로 연관관계가 있는 객체가 필드에 존재하게 되는데, 이 연관관계와 상관없이 즉시 해당 엔티티의 값만 조회하고 싶거나, 연관관계를 가진 테이블의 값도 조회하고 싶은 경우 등의 조건을 만족하기 위해 등장한 개념이 지연로딩과 즉시로딩이다.

다대다 매핑

다대다 연관관계는 실무에서 거의 사용되지 않는 관계 구성이다. 각 엔티티에서 서로를 리스트로 가지는 구조가 만들어지는데, 이 경우에는 중간 엔티티 테이블을 생성해 다대다를 일대다 또는 다대일로 해소하여 처리한다.

영속성 전이(cascade)

특정 엔티티의 영속성 상태를 변경할 때 그 엔티티와 연관된 엔티티의 영속성에도 영향을 미쳐 상태를 변경하는 것을 의미한다. 각 연관관계의 어노태이션에 cascade 라는 속성으로 포함되어 있다.

cascade의 종류

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

타입의 종류에서 알 수 있듯 엔티티 생명주기와 연관이 있다. 상태 변경이 일어나면 매핑으로 연관된 엔티티에도 동일한 동작이 일어나도록 전이를 발생시키는 것이다.

고아 객체(orphan)

고아 객체란 부모 엔티티와 연관관계가 끊어진 엔티티를 의미한다. JPA 에는 이러한 고아 객체를 자동으로 제거하는 기능이 있으며, 자식 엔티티가 다른 엔티티와 연관관계를 가지고 있다면 사용하지 않는 것이 좋다. 연관관계 어노태이션에 orphanRemoval = true 속성을 추가하는 방식으로 기능을 추가한다.

profile
똑똑한 사람이 되게 해주세요

0개의 댓글