[JPA] 연관관계 매핑 - 양방향 연관관계

Bam·2025년 5월 18일
0

Spring

목록 보기
56/73
post-thumbnail

양방향 연관관계

한 쪽 엔티티에서 다른쪽 엔티티에도 접근 가능하고, 그 반대도 성립하는 연관관계를 양방향 연관관계라고 합니다.

@OneToMany

단방향 연관관계 포스팅에서 처럼 1:N(일대다) 연관관계에 대해서만 설명합니다.
일대일 관계는 @OneToOne을 사용해주세요.

지난 단방향 연관관계 포스팅에서 엔티티는 Order 객체만 단방향 연관관계 매핑 @ManyToOne 정보를 가지고 있기 때문에 자바 코드상으로는 Order만 Customer를 알고 있고 그 반대로는 성립하지 않습니다.

@Entity
@Table(name = "customers_table")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "c_id", nullable = false)
    private Long id;

    @Column(name = "name", nullable = false, length = 100)
    private String name;
    
    //생성자 및 getter
}
@Entity
@Table(name = "orders_table")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "o_id", nullable = false)
    private Long id;

    @Column(name = "order_date", nullable = false)
    private LocalDateTime orderDate;

    /**
     * 단방향 @ManyToOne
     * orders_table.c_id → customers_table.c_id 외래 키 매핑
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "c_id", nullable = false)
    private Customer customer;
    
    //생성자 및 getter
}

이 상태에서 양방향 연관관계를 설정하고 싶다면 Customer@OneToMany를 추가함으로써 Customer에도 Order 엔티티를 알 수 있도록 설정합니다.

@Entity
@Table(name = "customers_table")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "c_id", nullable = false)
    private Long id;

    @Column(name = "name", nullable = false, length = 100)
    private String name;
    
    /**
     * 양방향 @OneToMany 
     * Order엔 @ManyToOne Customer 매핑
     */
    @OneToMany(mappedBy = "customer")
    private Order order;
    
    //생성자 및 getter
}

위와 같이 코드를 수정하면 Customer도 Order에 대한 정보를 가지고 있고 Order도 Customer를 가진 상태가 됩니다.

이때 주의할 점은 단뱡향이든 양방향이든 데이터베이스의 테이블은 애초부터 양방향 연관관계만을 지원하고 있었다는 점입니다.

외래 키 c_id를 이용해서 customers_table JOIN orders_table, orders_table JOIN customers_table 둘 다 가능하기 때문에 DB 테이블은 애초부터 양방향 연관관계였습니다.

mappedBy 속성

사실 설명은 양방향 연관관계로 했지만 자바 객체에서는 양방향 연관관계라는 개념이 존재하지 않습니다. 대신 단방향 연관관계를 서로 가리키도록 설정해서 양방향 연관관계를 구현하게 된 것 입니다.

이때 사용하는 것이 mappedBy 속성입니다. mappedBy는 반대쪽 매핑의 필드 이름을 설정합니다.

//Order.java
@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "c_id", nullable = false)	//매핑 대상의 기본 키 매핑
    private Customer customer;

//Customer.java
@OneToMany(mappedBy = "customer")	//Order의 Customer 정보를 담은 필드명 매핑
    private Order order;

여기서 아까 객체와 DB 테이블의 연관관계 시스템을 되돌아보면 한 가지 모순점이 발생합니다. 양방향 연관관계에서 객체는 서로가 서로를 참조하고, 테이블은 외래키 하나만을 이용해서 참조합니다.

그렇다면 테이블에서 사용될 외래 키는 두 객체의 연관관계 필드(Customer? or Order?) 중 어떤 것으로 설정해야하는 것일까요?

연관관계의 주인

상기한 문제를 해결하기 위해 양방향 연관관계에서는 연관관계의 주인을 지정해야합니다.

연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리하게 됩니다. 반대로 주인이 아닌 엔티티는 오직 읽기 작업만 수행할 수 있습니다.

이때 연관관계의 주인을 설정하게 되는 속성이 바로 위에서 본 mappedBy 속성입니다. mappedBy는 다음 동작을 통해 연관관계의 주인을 설정하게 됩니다.

  • 연관관계의 주인은 mappedBy 속성을 사용하지 않는다.
  • 주인이 아닌 엔티티는 mappedBy 속성을 통해 연관관계 주인 엔티티를 명시한다.
//Order.java
@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "c_id", nullable = false)
    private Customer customer;

//Customer.java
@OneToMany(mappedBy = "customer")
    private Order order;

즉, 위 코드에서 mappedBy의 매핑 대상이 되는 Order가 연관관계의 주인이 되고 mappedBy 속성이 적힌 필드를 가지고 있는 Customer가 주인이 아닌 엔티티가 되는 것 입니다.

주인이 아닌 엔티티는 읽기만 가능하다고 했지만 그렇다고 필드만 설정한 뒤 아무런 값도 넣지 않아도 된다는 것은 아닙니다.

엔티티 조작 시에 null 값 등의 문제를 불러일으킬 수 있기 때문에 양방향 연관관계를 설정했다면 반드시 주인과 주인이 아닌 쪽의 엔티티에 모두 값을 입력하고 사용하는 것이 안전합니다.

연관관계의 주인 정하기

연관관계의 주인을 정하는 법은 간단합니다.데이터베이스 테이블에서 외래 키를 가지고 있는 쪽이 연관관계의 주인입니다.

데이터베이스의 N:1(다대일) 관계에서는 항상 다(N)인 쪽이 외래 키를 가지게 되므로 다 쪽이 항상 연관관계의 주인이 됩니다. 그렇기 때문에 자바 객체에서는 @ManyToOne 매핑 필드를 가진 엔티티가 연관관계의 주인이 됩니다.

위와 같은 이유로 mappedBy 속성은 @ManyToOne에서 사용할 수 없습니다.

0개의 댓글