Entity 연관관계

앞고기랑 소금·2024년 8월 26일

스파르타 TIL

목록 보기
29/43

29일차

Entity 연관관계 <방향>

  • DB에선 어떤 테이블을 기준으로 잡아도 JOIN을 통해 조회할수있다

    DB 테이블에서는 테이블 사이의 연관관계를 FK(외래 키)로 맺을 수 있고 방향 상관없이 조회가 가능

  • A EntityB Entity를 참조하고 B Entity는 참조하지 않을경우 한쪽에서만 참조하는 단방향 관계가 됀다.
  • 양방향은 A Entity, B Entity 서로 참조하면 양방향 관계인것이다.
  • 방향이 있기에 외래키 설정, 참조 설계를 잘 고민해봐야한다.

1 : 1 관계

  • @OneToOne 애너테이션은 1 대 1 관계를 맺어주는 역할

  • @JoinColumn()은 외래 키의 주인이 활용하는 애너테이션

    • 컬럼명, null 여부, unique 여부 등을 지정할 수 있다.
  • 단방향 관계

    • Entity에서 외래 키의 주인은 일반적으로 N(다)의 관계인 Entity 이지만 1 대 1 관계에서는 외래 키의 주인을 직접 지정해야한다.
    • 외래 키의 주인만 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성을 설정

    A가 외래 키 주인인 경우

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne
    @JoinColumn(name = "b_id")
    private B b;
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
  • 양방향 관계
    • 양방향 관계에서 외래 키의 주인을 지정해 줄 때 mappedBy 옵션을 사용한다.

      • mappedBy의 속성값은 외래 키의 주인인 상대 Entity의 필드명을 의미
    • 외래 키의 주인은 상대 Entity 타입의 필드를 가지면서 @JoinColumn()을 활용하여 외래 키의 속성을 설정

      • 상대 Entity는 외래 키의 주인 Entity 타입의 필드를 가지면서 mappedBy 옵션을 사용하여 속성 값으로 외래 키의 주인 Entity에 선언된 @JoinColumn()으로 설정되고 있는 필드명을 넣어주면 됨.

주의사항

  • 외래 키의 주인 Entity에서 @JoinColumn() 애너테이션을 사용하지 않아도 default 옵션이 적용되기 때문에 생략이 가능
    • 다만 1 대 N 관계에서 외래 키의 주인 Entity@JoinColumn() 애너테이션을 생략한다면 JPA가 외래 키를 저장할 컬럼을 파악할 수가 없어서 의도하지 않은 중간 테이블이 생성됨
    • 따라서 외래 키의 주인 Entity에서 @JoinColumn() 애너테이션을 활용하시는게 좋다.
  • 양방향 관계에서 mappedBy 옵션을 생략할 경우 JPA가 외래 키의 주인 Entity를 파악할 수가 없어 의도하지 않은 중간 테이블이 생성되기 때문에 반드시 설정해주시는게 좋다

A가 외래 키 주인인 경우

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne
    @JoinColumn(name = "b_id")
    private B b;
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToOne(mappedBy = "b")
    private A a;
}

B가 외래 키 주인인 경우

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(mappedBy = "a")
    private B b;
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToOne(mappedBy = "b")
    @JoinColumn(name = "a_id")
    private A a;
}

N : 1 관계

  • @ManyToOne 애너테이션은 N 대 1 관계를 맺어주는 역할
  • 단방향 관계

A Entity가 N의 관계로 외래 키의 주인

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "b_id")
    private B b;
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}
  • 양방향 관계

A Entity가 N의 관계로 외래 키의 주인

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "b_id")
    private B b;
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToMany(mappedBy = "b")
    private List<A> aList = new ArrayList<>();
}

양방향 참조를 위해 A Entity에서 Java 컬렌션을 사용하여 B Entity 참조

1 : N 관계

  • @OneToMany 애너테이션은 1 대 N 관계를 맺어주는 역할
  • 단방향 관계
    • 외래 키를 관리하는 주인은 B Entity이지만 실제 외래 키는
      A Entity가 가지고 있다
      • 1 : N에서 N 관계의 테이블이 외래 키를 가질 수 있기 때문에 외래 키는 N 관계B 테이블에 외래 키 컬럼을 만들어 추가하지만 외래 키의 주인인 A Entity를 통해 관리

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany
    @JoinColumn(name = "b_id")
    private List<B> bList = new ArrayList<>();
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}

단점
외래 키를 A Entity가 직접 가질 수 있다면 INSERT 발생 시 한번에 처리할 수 있지만 실제 DB에서 외래 키를 B 테이블이 가지고 있기 때문에 추가적인 UPDATE가 발생된다는 단점이 존재

  • 양방향 관계
    • 1 대 N 관계에서는 일반적으로 양방향 관계가 존재하지 않는다
    • 1 대 N 관계에서 양방향 관계를 맺으려면 A Entity를 외래 키의 주인으로 정해주기 위해 B Entity에서 mappedBy 옵션을 사용해야 하지만 @ManyToOne 애너테이션은 mappedBy 속성을 제공하지 않는다
    • N 관계의 EntityB Entity에서 @JoinColuminsertableupdatable 옵션을 false로 설정하여 양쪽으로 JOIN 설정을 하면 양방향처럼 설정할 수는 있다
    // B Entity
     @Entity
     @Table(name = "b")
     public class B {
    	 @Id
    	 @GeneratedValue(strategy = GenerationType.IDENTITY)
    	 private Long id;
      
    	 @ManyToOne(mappedBy = "b")
    	 @JoinColumn(name = "a_id", insertable = false, updatable = false)
     	 private A a;
     }

N : M 관계

  • @ManyToMany 애너테이션은 N 대 M 관계를 맺어주는 역할
  • 단방향 관계
    • N : M 관계를 풀어내기 위해 중간 테이블(c)을 생성하여 사용

A Entity가 외래 키의 주인

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany
    @JoinTable(name = "c", // 중간 테이블 생성
    joinColumns = @JoinColumn(name = "a_id"), // 현재 위치인 A Entity 에서 중간 테이블로 조인할 컬럼 설정
    inverseJoinColumns = @JoinColumn(name = "b_id")) // 반대 위치인 B Entity 에서 중간 테이블로 조인할 컬럼 설정
    private List<B> bList = new ArrayList<>();
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
}

생성되는 중간 테이블을 컨트롤하기 어렵기 때문에 추후에 중간 테이블의 변경이 발생할 경우 문제가 발생할 가능성이 있다

  • 양방향 관계
    • 반대 방향인 B Entity@ManyToManyA Entity를 연결하고 mappedBy 옵션을 설정하여 외래 키의 주인을 설정하면 양방향 관계 맺음이 가능

A Entity가 외래 키의 주인

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToMany
    @JoinTable(name = "c", // 중간 테이블 생성
    joinColumns = @JoinColumn(name = "a_id"), // 현재 위치인 A Entity 에서 중간 테이블로 조인할 컬럼 설정
    inverseJoinColumns = @JoinColumn(name = "b_id")) // 반대 위치인 B Entity 에서 중간 테이블로 조인할 컬럼 설정
    private List<B> bList = new ArrayList<>();
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToMany(mappedBy = "bList")
    private List<A> aList = new ArrayList<>();
}
  • @ManyToMany 안쓰고 중간 테이블 사용하기
    • 중간 테이블 C를 직접 생성하여 관리하면 변경 발생 시 컨트롤하기 쉽기 때문에 확장성에 좋다

중간 테이블 C Entity가 외래 키의 주인

// A Entity
@Entity
@Table(name = "a")
public class A {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "a")
    private List<C> cList = new ArrayList<>();
}

// B Entity
@Entity
@Table(name = "b")
public class B {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToMany(mappedBy = "b")
    private List<C> cList = new ArrayList<>();
}

// C Entity
@Entity
@Table(name = "c")
public class C {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "a_id")
    private A a;

    @ManyToOne
    @JoinColumn(name = "b_id")
    private B b;
}

0개의 댓글