티스토리 뷰
JPA 양방향관계, 즉시로딩과 지연로딩
DB를 사용하다보면 필연적으로 여러 테이블을 설계하게 된다.
그리고 여러 테이블의 연관 관계를 이용하여 원하는 데이터를 DB로부터 가져오게 된다.
예를들어, 배달앱이라고 가정했을 때 유저의 정보를 저장하는 User
테이블과 주문 정보를 저장하는 Order
테이블이 존재 할 것이다.
그리고 유저는 여러개의 주문을 가질 수 있으며, 주문은 하나의 유저에게 속하게 된다.
이러한 관계를 유저 입장에서는 주문과 1:N
관계이며 주문 입장에서는 유저와 N:1
관계를 가지게 된다.
그리고 이를 클래스로 표현하면 다음과 같다.
@Entity
@Getter
@Setter
public class User {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
@Column
private String name;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
@Entity
@Getter
@Setter
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
}
여기서 잘 봐야할 부분은 User
클래스에 있는 orders
필드와 Order
클래스에 있는 member
필드이다.
User
는 Order
와 1:N관계이므로 @OneToMany
어노테이션을 사용했으며 Order는 그와 반대로 @ManyToOne
어노테이션을 사용했다.
일반적으로 외래키를 가지고 있는쪽이 연간관계의 주인이므로 외래키를 가지고 있는 Order
쪽에서 @JoinColumn
어노테이션을 통해 User의 PK와 매칭해준다.
User
쪽에서는 Order
의 member
필드에 매칭되었음을 알리기 위해 @OneToMany(mappedBy = "member")
로 설정하자.
만약 @JoinColumn
을 사용하지않으면 연관관계를 위한 테이블이 새롭게 생성되니 불필요한 테이블 생성을 막기위해 @JoinColumn
을 통해 User의 PK에 매칭하자.
양방향 관계 설정 이유
위 클래스를 잘보면 User
에는 Order
정보가 있고, Order
에는 User
정보가 있다.
하지만 실제로 DB 테이블의 구조를 보면 User
쪽에서는 Order
의 정보를 가지고 있을 필요가 없다. Order
에 존재하는 User
의 PK를 통해 참조할 수 있기 때문이다.
이처럼 양쪽에 관계를 설정하는 것을 양방향 관계
라고 하며 한쪽에만 관계를 설정하는 것은 단방향 관계
라고 한다.
그렇다면 왜 양방향 관계
로 설정해야 할까?
단방향 관계에서 User
와 Order
를 정보를 저장한다고 해보자.
Order
를 저장하는 시점에 User
의 정보를 모르므로 일단 Order
를 저장하고 이후에 User
의 PK를 Update해야 한다.
따라서, 데이터를 insert한 이후에 FK값을 설정해주는 추가적인 update
쿼리가 나가게 된다.
하지만 양방향 관계를 설정했을 경우 Order
를 저장하는 시점에 유저의 id를 알 수 있으므로 추가적인 update
쿼리가 나가지 않는다.
즉시로딩과 지연로딩
현재 User
와 Order
는 연관관계를 맺고 있다.
만약 Order
의 정보가 필요하다고하자. 이때, Order
의 정보를 가져올 때 User
의 정보도 함께 가져와야 할까?
물론 상황에 따라 다를 수 있다. 어떤 때에는 주문 정보와 유저의 정보가 함께 필요한 경우가 있을 수 있다.
하지만 반대로, 단순히 Order
의 정보만 필요한 경우가 있을 수 있다.
만약 즉시로딩
관계로 설정되어 있다면 Order
가 조회되는 시점에 연관관계로 맺어진 Member
도 DB로 부터 조회하게 된다. 하나의 쿼리로 Order의 정보와 User의 정보까지 가져오므로 효율적이라고 느껴질 수 있다.
하지만
"select o from Order o"
이런 쿼리를 사용하면 어떻게 될까
먼저 Order의 데이터를 모두 가져올 것이다. 그런데 Order의 데이터를 가져와 놓고 보니 연관관계 설정된 필드가 있고, 그 설정이 즉시로딩
으로 설정되어 있다.
그렇다면 이때, 연관관계 설정된 모든 User
의 데이터까지 가져오게 된다.
즉 쿼리는 단지 1개를 날렸을 뿐인데, 그와 연관된 데이터를 가져오기 위해 N개의 추가 쿼리가 나가야 하는 상황이다. 이를 JPQL N+1
문제라고 한다.
이러한 문제를 해결하기 위해서는 즉시로딩
이 아닌 지연로딩
을 사용하자.
그럼 연관관계가 설정되어 있더라도 그 데이터를 즉시 가져오지 않는다.
데이터를 흉내내는 프록시 객체
를 가져온다음, 실제로 그 데이터가 필요한 시점에 쿼리가 나가므로 더 효율적이라고 할 수 있다.
그렇다면 지연 로딩
으로 설정은 해두었지만, 특정 경우에 하나의 쿼리로 연관관계를 조회하고 싶을 때는 어떻게 하면 될까?
이때는 fetch join
이나 엔티티 그래프
기능을 사용하면 된다.
JPA에서 ManyToOne 이나 OneToOne 은 기본적으로 즉시로딩 으로 설정되어 있다. 따라서 지연로딩 으로 설정하기 위해서는 @ManyToOne(fetch = FetchType.LAZY)
와 같이 설정하자.
'JPA' 카테고리의 다른 글
[JPA] 변경감지와 병합(merge)을 통한 엔티티 수정 (0) | 2022.04.15 |
---|
- Total
- Today
- Yesterday
- 세그먼트 트리
- BFS
- 시뮬레이션
- 자바스크립트
- 알고리즘
- node.js
- 그래프
- 벨만포드
- 컴퓨터 구조
- Computer Architecture
- nodeJS
- 자바
- 컴퓨터 통신
- 예외처리
- 중앙대학교
- dfs
- ReactNative
- 투포인터
- 재귀
- 백트래킹
- 그리디
- 동적계획법
- typeORM
- nest.js
- 스레드
- boj
- 구현
- 백준
- nestjs
- java
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |