Skip to main content Link Search Menu Expand Document (external link) Copy Copied

Entity 연관 관계

  • 1:N 관계 예시
    • 고객(1):음식(N)
  • N:M 관계 예시
    • 고객과 음식(N:M)

N:M 관계는 중간 테이블을 사용하여 해결할 수 있다.


테이블 간 방향

데이터베이스에서는 테이블 간의 연관 관계를 외래 키(FK)로 설정할 수 있으며, 방향 없이 조회가 가능하다. 어떤 테이블에서 JOIN을 하더라도 동일한 결과를 얻을 수 있기 때문이다.
그러나 서버에서는 @Entity를 사용해 Java 컬렉션을 통해 한 로우에 여러 값을 가질 수 있다.

@OneToMany
private List<Entity> entityList;

이를 통해 N:1 양방향 관계를 설정할 수 있다.

엔티티 클래스에서는 상대 엔티티 정보를 참조해야 조회가 가능하며, 이를 위해 양방향 관계로 설정해야 한다. 엔티티 클래스 간에는 방향이라는 개념이 생겨난다.


1 대 1 관계

@OneToOne 어노테이션은 1:1 관계를 설정한다.
외래 키의 주인은 외래 키를 등록, 수정, 삭제할 수 있으며, 주인이 아닌 쪽은 읽기만 가능하다.
@JoinColumn은 외래 키의 주인이 사용하는 어노테이션으로, 컬럼명, null 여부, 유니크 여부 등을 설정할 수 있다.

단방향 설정
외래 키의 주인 엔티티에 @OneToOne@JoinColumn(name="column_id")을 설정한다.

양방향 설정
외래 키의 주인 엔티티에는 @OneToOne@JoinColumn(name="column_id")를 설정하고,
주인이 아닌 상대 엔티티에는 @OneToOne(mappedBy = "column")을 설정하여 외래 키 주인 엔티티의 필드를 참조한다.

주의: 외래 키의 주인 엔티티에서 @JoinColumn을 생략할 수 있지만 1:N 관계처럼 중간 테이블이 생성될 수 있으므로 특별한 목적이 없다면 1:1 관계에서도 @JoinColumn을 표기하는 것이 좋다.


N 대 1 관계

@ManyToOne 어노테이션은 N:1 관계를 설정한다.

단방향 설정
외래 키의 주인 엔티티에 @ManyToOne@JoinColumn(name="column_id")를 설정한다.

양방향 설정
양방향 참조를 위해 상대 엔티티는 자바 컬렉션을 사용하여 주인 엔티티를 참조한다.
주인 엔티티에는 @ManyToOne@JoinColumn(name="column_id"),
상대 엔티티에는 @OneToMany(mappedBy = "column")을 설정한다.


1 대 N 관계

@OneToMany 어노테이션은 1:N 관계를 맺어준다. @OneToMany 어노테이션은 1:N 관계를 설정한다.

단방향 설정
주인 엔티티에 @OneToMany@JoinColumn(name="column_id")을 설정하여 자신의 id로 참조한다.
그러나 실제 테이블에는 컬럼이 없어 insert 후 update가 되는 문제가 발생할 수 있다.

양방향 설정
일반적으로 양방향 관계는 존재하지 않는다. 양쪽에서 억지로 설정할 수 있지만 권장하지 않는다.
주인 엔티티에는 @OneToMany와 자신의 id를 담은 @JoinColumn(name="column_id"),
상대 엔티티에는 @ManyToOne@JoinColumn(name="column_id", insertable = false, updatable = false)을 설정한다.


N 대 M 관계

@ManyToMany 어노테이션은 N:M 관계를 설정한다.
JPA는 중간 테이블을 자동으로 생성하지만, 중간 테이블에 PK가 없어 컨트롤이 어렵다.

단방향 설정
주인 엔티티에 @ManyToMany@JoinTable(name="table", joinColumns = @JoinColumn(name = "column_id"), inverseJoinColumns = @JoinColumn(name = "column_id"))을 설정한다.

양방향 설정
주인 엔티티에 @ManyToMany@JoinTable(name="table", joinColumns = @JoinColumn(name = "column_id"), inverseJoinColumns = @JoinColumn(name = "column_id")),
상대 엔티티에는 @ManyToMany(mappedBy = "column")을 설정한다.


중간 테이블 직접 생성

중간 테이블은 외래 키의 주인 역할을 하며, N:1 관계로 풀어낸다. 직접 생성하게되면 pk나 date 등 원하는 컬럼을 추가하며 컨트롤할 수 있다.

주인 엔티티에는 @ManyToOne@JoinColumn(name = "column1_id"), @ManyToOne@JoinColumn(name = "column2_id")을 설정한다.
상대 테이블 1과 2는 각각 @OneToMany(mappedBy = "column1"), @OneToMany(mappedBy = "column2")을 설정하여 양방향 관계를 설정한다.


지연 로딩 (Lazy Loading)

JPA에서는 연관 관계가 설정된 엔티티의 데이터를 언제 가져올지를 결정할 수 있다. 이 방법을 FetchType이라고 한다.

  • LAZY (지연 로딩): 필요한 시점에 데이터를 가져온다.
  • EAGER (즉시 로딩): 즉시 데이터를 가져온다.

기본 설정

  • @~Many로 끝나는 어노테이션은 리스트를 반환한다.
    • @OneToMany
    • 기본값은 LAZY (지연 로딩)
  • @~One으로 끝나는 어노테이션은 객체 하나를 반환한다.
    • @ManyToOne
    • 기본값은 EAGER (즉시 로딩)

지연 로딩은 영속성 컨텍스트의 기능 중 하나이므로, 지연 로딩이 설정된 엔티티를 조회하려면 영속성 컨텍스트가 반드시 존재해야 한다. Spring Framework에서는 @Transactional을 적용해 영속성 컨텍스트를 유지할 수 있다.


영속성 전이 (Cascade)

데이터베이스에서 on delete Cascade 기능이 있는 것처럼, JPA에서도 Cascade 옵션을 통해 연관된 엔티티를 자동으로 처리할 수 있다.

예를 들어, 특정 엔티티를 저장할 때 그와 연관된 엔티티도 함께 저장하려면 연관 관계 어노테이션에 cascade = CascadeType.PERSIST를 추가한다.

@OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST)

다양한 옵션을 조합할 수도 있다.

  • cascade = {CascadeType.PERSIST, CascadeType.REMOVE}

고아 Entity 삭제 (Orphan Removal)

JPA에서는 연관 관계를 제거함으로써 해당 데이터를 삭제할 수 있는 orphanRemover 기능을 제공한다.

CascadeType.REMOVE처럼 연관된 엔티티를 삭제하면 자동으로 연결된 엔티티도 함께 제거된다. 이 기능을 사용할 때는 신중해야 한다. 특히 다른 곳에서 해당 엔티티를 참조하고 있는지 확인해야 하며, @ManyToOne 관계에서는 사용하지 않는 것이 좋다.


실습자료

GitHub