❗️책 자바 ORM 표준 JPA 프로그래밍 공부한 내용을 정리한 글 입니다.
엔티티를 조회할 때 연관관계
에 있는 엔티티를 조회하는 시점
에 따라서 즉시 로딩 또는 지연 로딩으로 나눌 수 있다. 이름에서도 쉽게 유추할 수 있듯이 즉시 로딩은 엔티티를 조회할 때 연관되어 있는 엔티티와 함께 데이터베이스에서 조회하는 것을 의미하고, 지연 로딩은 대상 엔티티와 연관되어 있는 엔티티를 실제 사용 시점에 데이터베이스에서 조회하는 특징이 있다.
즉시 로딩은 @ManyToOne
의 fetch
속성을 FetchType.EAGER
로 지정함으로써 설정할 수 있다. 예를 들어 Member 와 Team 엔티티가 있고, Member 가 Team 에 소속되는 관계 + 즉시 로딩으로 설정되어 있으면 Member 엔티티 조회 시점에 Member 엔티티와 연관관계를 맺고 있는 Team 엔티티까지 함께 데이터베이스로부터 조회힌다.
대부분의 JPA 구현체는 즉시 로딩을 최적화 하기 위해 가능하면 조인 쿼리를 사용한다. (자바 ORM 표준 JPA 프로그래밍 - 김영한 저)
조인 쿼리를 사용하여 두 대상을 함께 조회하는 것이다. 이 때 연관관계를 맺는 방식 (비즈니스 요구사항) 에 따라서 INNER JOIN
와 LEFT OUTER JOIN
중 어떤 조인 전략을 사용할 지가 결정되는데 INNER JOIN
이 상대적으로 최적화 및 성능 측면에서 유리한 측면이 있기 때문에 이 두 방식 중 어떤 것이 사용 되는 지를 결정하는 요인
에 대해서 알 필요가 있다.
Member 와 Team 이 있을 때 다음과 같이 비즈니스 요구사항을 나눌 수 있다.
① Member 는 Team 에 소속되지 않을 수 있다. (무소속 상태 가능) ② Member 는 반드시 하나의 Team 에 소속돼야 한다. (무소속 상태 불가능)
① 의 경우,
Member 는 무소속 상태로 존재할 수 있고 따라서 아래 테이블 생성 DDL 에서 볼 수 있듯이 team_id
컬럼을 NULL
로 설정한다.
CREATE TABLE member
(
`id` BIGINT NOT NULL AUTO_INCREMENT,
`team_id` BIGINT NULL,
PRIMARY KEY (id),
FOREIGN KEY (team_id) REFERENCES team (id)
);
JPA 에서는 @JoinColumn
의 nullable
속성 값을 true
로 설정하면 된다.
그렇다면 ① 의 경우에는 INNER JOIN
과 LEFT OUTER JOIN
중 어떤 것을 사용할까?
[ 그림 2 ] 에서 각각의 쿼리 결과 조회 범위를 볼 수 있는데 ① 의 경우 INNER JOIN
을 사용 했을 때 의도한 결과가 나오지 않을 가능성이 있다.
Member 리스트를 조회할 때 즉시 로딩 관계에 있는 Team 으로 인해 조인 쿼리를 생성해서 Member 와 Team 을 함께 조회하는데 이 때 INNER JOIN
을 사용하게 되면 팀에 소속되지 않은 Member 는 조회 대상에서 제외되는 문제가 있다. (팀 소속 관계 없이 Member 리스트를 조회하는 상황에서)
따라서 ① 의 경우에 대해 JPA 는 [ 그림 2 ] 의 LEFT OUTER JOIN
을 통해 팀에 소속 여부와 관계 없이 Member 를 조회하도록 디폴트 설정이 되어 있다.
CREATE TABLE member
(
`id` BIGINT NOT NULL AUTO_INCREMENT,
`team_id` BIGINT NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (team_id) REFERENCES team (id)
);
이와 반대로 ② 의 경우, team_id
컬럼을 NOT NULL
로 설정하고 이를 JPA 에서는 @JoinColumn
의 nullable
속성 값을 false
로 설정하면 된다. 무소속 상태의 Member 가 없다는 전제 이므로 보다 Member 엔티티 조회 시 INNER JOIN
을 사용해 Team 엔티티를 함께 조회 하더라도 무방하다.
프록시 객체를 사용하면 객체 탐색을 하는 시점에 연관관계에 있는 엔티티를 데이터베이스에서 조회할 수 있다.
JPA Proxy (프록시) 글에서 프록시를 위와 같이 설명 했다. 지연 로딩 전략에서 프록시 객체가 사용된다. 지연 로딩은 @ManyToOne
의 fetch
속성을 FetchType.LAZY
로 지정 함으로써 설정할 수 있다.
SELECT * FROM member WHERE member_id = "member1"
Member 엔티티를 조회 했을 때 지연 로딩 전략으로 연관관계 매핑을 했으므로 Team 엔티티는 데이터베이스에서 조회하지 않고 프록시 객체 형태로 생성한다. 그리고 team.getName()
등의 연관관계에 있는 엔티티를 실제 사용 (= 객체 그래프 탐색) 하는 시점에 프록시 객체 초기화
가 진행되고, 아래 쿼리를 통해서 Team 엔티티를 조회한다.
SELECT * FROM team WHERE team_id = "team1"
자바 ORM 표준 JPA 프로그래밍 | 8장 프록시와 연관관계 정리