🟩 단방향 연관관계
01. "객체 연관관계"와 "테이블 연관관계"
01-01. 상품(Product)과 카테고리(Category)의 관계
- 상품은 하나의 카테고리에 속할 수 있다.
- 여러 상품이 하나의 카테고리에 속할 수 있다.
- 상품과 카테고리는 N:1(다대일) 관계이다.
01-02. 객체 연관관계
- 상품과 카테고리는 필드(Product.category)로 연관관계를 맺는다.
- 상품과 카테고리는 단방향 관계이다.
- 단방향 관계인 이유
- Product.category를 통해서 상품의 카테고리를 알 수 있다.
- Category는 어떤 상품이 존재하는지 알 수 없다.
- 오직 상품만이 카테고리를 알 수 있는 관계이다.
01-03. 테이블 연관관계
- 상품과 카테고리는 외래키(FK)로 연관관계를 맺는다.
- 상품 테이블과 카테고리 테이블은 양방향 관계이다.
- 양방향 관계인 이유
- 상품 테이블의 category_id를 이용해서 카테고리 테이블을 조회할 수 있다.
- 카테고리 테이블의 category_id를 이용해서 상품 테이블을 조회할 수 있다.
02. 객체 연관관계와 테이블 연관관계의 차이
02-01. 객체 연관관계
- 참조로 연관관계를 맺는다.
- 참조를 통한 연관관계는 항상 단방향이다.
- 참조가 양방향으로 존재한다는 것은 사실 단방향 관계가 2개 있다는 것을 의미한다.
02-02. 테이블 연관관계
- 테이블은 외래키(FK)로 연관관계를 맺는다.
- 테이블은 외래키 하나로 양방향으로 조인할 수 있다.
03. 객체 그래프 탐색과 조인
03-01. 객체 그래프 탐색
- 객체의 참조를 통해서 연관관계를 탐색하는 것을 "객체 그래프 탐색"이라고 한다.
03-02. 조인
- 테이블의 외래키를 통해서 연관관계를 탐색하는 것을 "조인"이라고 한다.
🟩 객체 관계 매핑 (@ManyToOne, @JoinColumn)
상품(Product)와 카테고리(Category)의 객체 관계를 매핑해보자.
먼저 예시 코드를 보고 이후에 매핑하기 위해 사용한 어노테이션(@ManyToOne, @JoinColumn)에 대해서 알아보자.
01. 상품, 카테고리 관계 매핑 예시 코드
- @ManyToOne : 연관관계를 매핑할 때 다중성을 나타낸다.
- @JoinColumn(name="category_id") : 외래키를 매핑할 때 사용한다. [생략 가능]
@Entity
public class Product {
@Id @GeneratedValue(strategy = IDENTITY)
private Long id;
private String title;
@ManyToOne
@JoinColumn(name="category_id")
private Category category;
}
@Entity
public class Category {
@Id @GeneratedValue(strategy = IDENTITY);
private Long id;
private String title;
}
02. @ManyToOne 어노테이션
- N:1(다대일) 관계에서 사용하는 어노테이션
속성 | 기능 | 기본값 |
optional | false : 연관된 엔티티가 항상 존재해야 한다. | true |
fetch | 글로벌 패치 전략 | @ManyToOne = FetchType.EAGER @OneToMany = FetchType.LAZY |
cascade | 영속성 전이 기능 | |
targetEntity | 연관된 엔티티의 타입 정보 설정 (컬렉션을 사용해도 제네릭으로 타입 정보를 알 수 있다.) |
03. @JoinColumn 어노테이션
- 외래키 매핑할 때 사용하는 어노테이션
속성 | 기능 | 기본값 |
name | 매핑할 외래키(FK) 이름 | 필드명 + _ + 참조하는 테이블의 기본키 컬럼명 |
referencedColumnName | 외래키가 참조하는 대상 테이블의 컬럼명 | 참조하는 테이블의 기본키 컬럼명 |
foreignKey | 외래키 제약 조건을 직접 지정. 테이블 생성할 때만 사용 |
|
nuique nullable inertable updatable columnDefinition table |
@Column 속성과 동일 |
03-01. @JoinColumn 생략할 경우
- @JoinColumn를 생략할 경우 기본 전략을 사용한다.
// 필드명 + _ + 참조하는 테이블의 컬럼명
// category_id
@ManyToOne
private Category category;
🟩 양방향 연관관계
이전까지 상품(Product)가 카테고리(Category)를 접근할 수 있는 단방향 관계였다.
이번에는 카테고리에서 상품에 접근할 수 있도록 단방향 관계를 하나 더 만들자.
실제로 상품과 카테고리는 단방향 연관관계가 2개 있다는게 더 정확한 표현이지만 양방향 관계라고 부르고 있다.
01. 양방향 연관관계 매핑하기
상품과 카테고리가 양방향 관계를 갖도록 매핑하자.
상품에는 Product.category라는 필드를 두었을 때 카테고리에는 어떤 필드를 두어야할까?
다시 한번 생각해보면 상품과 카테고리는 N:1 관계였다.
여러 상품이 하나의 카테고리에 속할 수 있었다.
카테고리 입장에서는 여러 상품을 가지고 있을 수 있는 것이므로 컬렉션을 이용하면 된다.
@Entity
public class Product {
@Id @GeneratedValue(strategy = IDENTITY)
private Long id;
private String title;
@ManyToOne
@JoinColumn(name="category_id")
private Category category;
}
@Entity
public class Category {
@Id @GeneratedValue(strategy = IDENTITY);
private Long id;
private String title;
// 추가한다
@OneToMany(mappedBy = "category")
private List<Product> products = new ArrayList<>();
}
02. @OneToMany 어노테이션과 mappedBy 속성
Category 클래스 코드를 보면 @OneToMany 어노테이션과 그 속성으로 mappedBy를 이용했다.
@OneToMany
- 1:N 관계를 매핑하는 어노테이션
mappedBy 속성
- 양방향 매핑을 사용하고 있을 때 연관관계 주인을 정하는 속성이다.
- @OneToMany(mappedBy = "category")는 Product.category가 연관관계 주인임을 나타내고 있다.
🟩 연관관계 주인
테이블은 양방향 연관관계이다.
테이블은 외래키 하나로 두 테이블을 조인하여 어느 쪽에서든 조회할 수 있다.
하지만 객체는 테이블과 다르다.
객체는 양방향 연관관계라는 것이 사실 없고 단방향 관계가 2개 존재할 뿐이다.
두 객체간에 단방향 관계가 2개 존재할 때 문제로 인해서 연관관계 주인이 필요하다.
JPA에서는 객체의 참조가 둘이 있을 때 어떤 객체가 누구에게 외래키를 주는지 알 수 없기 때문이다.
01. 연관관계 주인만이 외래키를 관리한다. (등록, 수정, 삭제)
오직 연관관계 주인이 데이터베이스 연관관계와 매핑되어 외래키를 관리할 수 있다.
주인이 아닌 쪽은 단순히 읽기만 가능하다는 규칙이 있다.
상품이 연관관계 주인이라고 했을 때 Product.category를 통해서 상품의 카테고리를 수정할 수 있지만
주인이 아닌 카테고리는 Category.products.get(0).setTitle("새로운 상품명")이라는 동작을 수행해도 데이터베이스에 영향이 가지 않는다.
02. 연관관계 주인을 알려주는 mappedBy 속성
이러한 문제를 해결하기 위해서 mappedBy라는 속성이 있다.
상품, 카테고리 매핑 코드를 다시 보자.
카테고리와 상품의 연관관계에서 카테고리는 상품에 의해서 매핑되고 있음을, 즉 상품이 주인임을 의미하고 있다.
@Entity
public class Category {
@Id @GeneratedValue(strategy = IDENTITY);
private Long id;
private String title;
// 추가한다
@OneToMany(mappedBy = "category")
private List<Product> products = new ArrayList<>();
}
03. 양방향 매핑할 시에 연관관계의 주인
연관관계 주인은 테이블 외래키가 있는 곳으로 정하자.
1:N일 때 N이 있는 곳을 연관관계의 주인으로 두면 된다.
만약 반대로 연관관계 주인을 주게 된다면 외래키를 다른 테이블이 관리하게 되고 여러 문제가 발생할 수 있다.
더 자세한 이야기를 생략하겠다.