순간을 성실히, 화려함보단 꾸준함을

[JPA] @JoinColumn 확실히 알고가기!!! 본문

나의 개발 메모장

[JPA] @JoinColumn 확실히 알고가기!!!

폭발토끼 2022. 1. 12. 23:15

안녕하세요
오늘은 JPA 주제로 글을 써보도록 하겠습니다.

테이블들간 연관관계를 설정해 줄때 일대다(1:N) 관계일때 @JoinColumn 어노테이션을 사용해서 해당 컬럼의 이름을 설정해줍니다....

근데!!! 전 지금껏 이 @JoinColumn 의 역할에 대해서 너무 오해하고 있었습니다...ㅠㅠ

소스를 통해 설명하도록 하겠습니다.

@Entity
@Getter @Setter
public class Member {

    @Id @GeneratedValue
    @Column(name="member_id")
    private Long id;

    @NotEmpty
    private String name;

    //Embedded type 은 사용자가 직접 정의한 값 타입이다. 여기서 Embedded type을 사용하지 않으면,집 주소에 관한 정보
    //city,street,zipcode 를 전부 정의해 줘야 되는데 그러면 객체지향적이지 않고 응집력을 떨어뜨리는 원인이 된다.
    //때문에 Embedded type을 사용하여 코드를 좀 더 명확히 한 것이다.
    @Embedded
    private Address address;

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();
}

Member Entity 입니다.

@Entity
@Table(name="orders")
@Getter @Setter
public class Order {

    @Id @GeneratedValue
    @Column(name="order_id")
    private Long id;

    /*
        연관관계의 주인은 mappedby 를 사용하지 않는다.
        연관관계의 주인은 외래키가 있는 쪽이 주인이 된다.
    */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="member_id")
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

    /*
        1:1 관계에서 외래키는 주테이블에 존재하는 것으로 하자. 그게 제일 편하다.
    */
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name="delivery_id")
    private Delivery delivery;

    private LocalDateTime orderDate; //주문시간

    @Enumerated(EnumType.STRING)
    private OrderStatus status; //주문상태 [ORDER, CANCEL]

    ,,,
}

Order Entity 입니다.

이 두 Entity 간에 다른건 다 무시하시고 연관관계를 정의해준 부분만 보면 됩니다.


Member 와 Order는 일대다(1:N) 관계입니다.
그러니 외래키는 당연히 '다'쪽인 Order 테이블에 존재할 것이고, 외래키가 존재하는 곳이 곧 '연관관계의 주인' 이 되겠죠?
(만약 이 문장이 이해가 안가신다면 JPA 기본개념을 습득하셔야 합니다. 자바 ORM 표준 JPA 책을 꼭꼭 읽고 오세요)

그러면 'mappedBy' 옵션은 연관관계의 주인이 아닌 엔티티에 설정을 해주어야 하니, Member 엔티티에 mappedBy 옵션을 설정해주게 됩니다.

 

이때, '다'쪽 엔티티에서는 @JoinColumn 어노테이션을 설정해주는데 Join 이라는 단어때문에 전 혼동을 했었습니다.
그 이유는 상대 테이블의 컬럼명과 동일하게 적어주어서 Order 테이블이 Member 테이블과 1:N 관계라는 것을 알려주는 것 이구나!!!!라고 큰 착각을 했습니다.

즉, 위의 소스를 예를 들면 Member 엔티티의 id 필드는 테이블에 'member_id'라고 정의가 됩니다. 그러면 연관관계를 맺고 있는 Order 테이블의 member 에 @JoinColumn 의 name 속성을 똑같이 'member_id'라고 정의해 주어야 한다고 생각했던 것이죠.


그러나!!!!!절대 아닙니다. name 속성은 말 그래도 Order 엔티티에 존재하는 member 라는 필드를 어떤 이름으로 Order 테이블에 컬럼명으로 설정할 것인지를 나타내주는 것 입니다.


@JoinColumn(name="member_id") 라고 적었으면 그냥 Order 테이블에 member_id 라는 컬럼명이 정의되는 것이죠.

그러면, 이상한게 있습니다....대체 Order 엔티티는 어떻게 Member 엔티티와 연관관계를 맺고 있다는 것을 알수있을까요???


Member 엔티티야 mappedBy 옵션으로 알려준다 하지만 Order 엔티티는 어떠한 설정도 하지 않은 것 같은데 말이죠.....

바로, referencedColumnName 속성을 사용하면 됩니다.

(선택 사항) 이 외래 키 열이 참조하는 열의 이름입니다.
-여기에 설명된 경우가 아닌 엔터티 관계 매핑과 함께 사용하는 경우 참조된 열은 대상 엔터티의 테이블에 있습니다.
-단방향 OneToMany 외래 키 매핑과 함께 사용하는 경우 참조된 열은 원본 엔터티의 테이블에 있습니다.
-JoinTable 주석 내에서 사용되는 경우 참조된 키 열은 소유 엔터티의 엔터티 테이블에 있거나 조인이 역 조인 정의의 일부인 경우 역 엔터티에 있습니다.
-CollectionTable 매핑에서 사용되는 경우 참조된 열은 컬렉션을 포함하는 엔터티의 테이블에 있습니다.

 

뭐 여러가지 내용들이 정의되어 있는데, 알고가셔야 할 것은 referencedColumnName 속성을 생략하면 자동으로 대상 테이블의 pk 값으로 지정 되기 때문입니다.

그러니 생략해도 알아서 pk 값하고 잘 매핑이 되는 것이죠.
앞으로는 헷갈릴 일은 없겠습니다!