29일차
DB 테이블에서는 테이블 사이의 연관관계를 FK(외래 키)로 맺을 수 있고 방향 상관없이 조회가 가능
A Entity가 B Entity를 참조하고 B Entity는 참조하지 않을경우 한쪽에서만 참조하는 단방향 관계가 됀다.A Entity, B Entity 서로 참조하면 양방향 관계인것이다.@OneToOne 애너테이션은 1 대 1 관계를 맺어주는 역할
@JoinColumn()은 외래 키의 주인이 활용하는 애너테이션
단방향 관계
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;
}
@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 참조
@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 관계의 Entity인 B Entity에서 @JoinColum의 insertable 과 updatable 옵션을 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;
}@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에 @ManyToMany 로 A 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;
}