Categories
Algorithm🧩
백준 📝
BookReview📕
CleanCode✨
Network 📨
Database 🗄
DevOps☁️
에러 일기📕
Etc💬
Fishy Fish 🎣
Spring🌱
연관관계 매핑
연관 관계 매핑
- 연관관계: 어떤 엔티티를 중심으로 보느냐에 따라 연관관계 상태가 달라짐
- JPA에는 양방향, 단방향 관계가 존재
- 양방향: 두 엔티티가 서로의 엔티티를 참조
- 단방향: 한 쪽의 엔티티만 참조
- 연관관계 설정이 되면 한 테이블에서 다른 테이블의 기본값을 외래키로 갖게 된다.
일대일 매핑
@OneToOne: 다른 엔티티 객체를 필드로 정의했을 때 일대일 연관으로 매핑하기 위해 사용@JoinColumn: 매핑할 외래키 설정
단방향 매핑
@OneToOne
@JoinColumn(name = "product_number")
private Product product;
- @JoinColumn
- 이걸 선언하지 않으면 매핑하는 중간 테이블이 생긴다.
- 옵션
1) name: 의도한대로 설정하기 위해서는 name속성으로 칼럼명을 지정
2) referencedColumnName: 외래키가 참조할 상대 테이블의 칼럼명 지정
3) foreignKey: 외래키 제약 조건 설정 (unique, nullable 등)
- 실행 시
즉시 로딩+left outer join -
- @OneToOne의 fetch 전략: Eager(즉시 로딩)
- ProductDetail 객체와 Product 객체가 함께 조회 =
즉시 로딩
-
- @OneToOne 어노테이션 Optional
- true (nullable) 설정 - left outer join (default)
- false (not null) 설정 시 - inner join
양방향 매핑
양쪽에서 단방향으로 서로를 매핑하는 것을 의미
-
- 양쪽에서 단방향으로 매핑하니 left outer joind이 두 번 수행
- – 효율성 X
∴주인개념 등장 - 한 쪽의 테이블에서만 외래키를 바꿀 수 있도록 정함
– 양방향으로 매핑하되 한쪽에게만 외래키를 줘야 한다!
mappedBy속성 사용: 어떤 객체가 주인인가..
@OneToOne(mappedBy = "product")
@ToString.Exclude // 순환참조 제거
private ProductDetail productDetail;
- productDetail엔티티가 product 엔티티의 주인이라는 의미
- 양방향 연관관계 설정에서는 ToString 사용 시 순환참조 발생:
@ToString.Exclude사용
다대일, 일대다 매핑
다대일 단방향 매핑
- 상품과 공급업체 예시
- 공급업체 하나에서 상품 여러개 공급
- 상품 테이블 입장에서는 다대일, 공급업체 입장에서는 일대다 관계
...
public class Product extends BaseEntity {
...
@ManyToOne
@JoinColumn(name = "provider_id")
@ToString.Exclude // 순환참조 제거
private Provider provider;
}
- 일반적으로 외래키를 갖는 쪽이 주인 역할 수행 - 상품 엔티티가 공급업체 엔티티의 주인
- 쿼리로 데이터를 저장할때 외래키 부분에는 공급업체의 id 값만 들어감
다대일 양방향 매핑
- 공급업체를 통해 등록된 상품을 조회하기 위한 일대다 연관관계
...
public class Provider extends BaseEntity {
...
@OneToMany(mappedBy = "provider")
@ToString.Exclude // lombok에 의한 순환 참조 발생 가능성으로 설정
private List<Product> productList = new ArrayList<>();
}
- 여러 상품 엔티티가 포함될 수 있으므로 Collection 형식으로 필드 생성
-
OneToMany의 기본 fetch 전략은 Lazy(지연 로딩)
-
- mappedBy로 설정된 필드는 칼럼에 적용 X
- 양쪽에서 연관관계 설정하고 있을 때 RDBMS 형식처럼 사용하기 위해 mappedBy를 통해 외래키 관리 위임
-
- Provider 엔티티는 주인이 아니여서 외래키 관리불가능
- 따라서 provider 등록 후 Product에 객체를 설정하는 작업을 통해 DB에 저장
- provider Repository를 통해 연관관계가 매핑된 Product를 가져옴
일대다 단방향 매핑
- 상품과 상품의 카테고리 예제
... public Category { ... @OneToMany(fetch = FetchType.EAGER) @JoinColumn(name = "category_id") private List<Product> products = new ArrayList<>(); } - 이렇게 카테고리에서
@OneToMany로 일대다 단방향 연관관계 매핑 -
- 매핑의 주체가 아닌 반대 테이블에 외래키가 추가된다.
- 다른 테이블에 대한 update 쿼리 발생
다대다 매핑
실무에서 거의 사용 X
- 예시: 한 종류의 상품이 여러 생선업체 + 생산업체 한 곳이 여러 상품 생산
- 각 엔티티 서로가 리스트를 가지는 구조
- 교차 엔티티라고 부르는 중간 테이블을 생성해 다대다 관계를 일대다 또는 다대일 관계로 해소
다대다 단방향 매핑
...
public Producer extends BaseEntity {
...
@ManyToMany
private List<Product> products = new ArrayList<>();
}
@ManyToMany어노테이션 설정- 리스트로 필드를 가지는 객체는 외래키를 가지지 않아 @JoinColumn은 필요 없음
- 중간 테이블 생성되어 상품 테이블과 생선업체 태이블의 기본키를 가져와 외래키로 설정함
- 그 중간테이블에서 연관된 엔티티의 값을 가져옴
다대다 양방향 매핑
public class Product extends BaseEntity {
@ManyToMany
@ToString.Exclude
private List<Producer> producers = new ArrayList<>();
}
- 생산업체에 대한 다대다 연관관계 매핑
- DB 테이블 구조는 변경 X: 중간 테이블로 연관관계를 설정하기 때문
-
- 이렇게 중간 테이블로 관리할 경우 예기치 못한 쿼리 발행 가능
- 관리하기 힘든 포인트 발생
∴ 중간 테이블 생성 대신 일대다, 다대일로 연관관계를 맺을 수 있는 중간 엔티티로 승격시켜 JPA에서 관리할 수 있게 생성하는게 좋다.
영속성 전이
특정 엔티티의 영속성 상태를 변경할 때 그 엔티티와 연관된 엔티티의 영속성에도 영향을 미쳐 영속성 상태를 변경하는 것
- @OneToOne 어노테이션의 cascade(): 영속성 전이 설정에 활용
-
영속성 전이 타입의 종류
1) ALL: 모든 영속 상태 변경에 대해 영속성 전이 적용
2) PERSIST: 엔티티가 영속화할 때 연관된 엔티티도 함께 영속화
3) MERGE: 엔티티를 영속성 컨텍스트에 병합할 때 연관된 엔티티도 병합
4) REMOVE: 엔티티를 제거할 때 연관된 엔티티도 제거
5) REFRESH: 엔티티 새로고침 시 연관된 엔티티도 새로고침
6) DETACH: 엔티티를 영속성 컨텍스트에서 제외하면 연관된 엔티티도 제외 - 엔티티 생명주기와 연관
- cascade() 요소의 리턴 타입은 배열 형식: cascade 타입을 골라 각 상황에 적용 가능
- 한 엔티티가 cascade 요소의 값으로 주어진 영속 상태의 변경이 일어나면 매핑으로 연관된 엔티티에도 동일한 동작이 일어나도록 전이를 발생