Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save taekwon-dev/008a02157948a2fe16e29c90fa3e4bce to your computer and use it in GitHub Desktop.
Save taekwon-dev/008a02157948a2fe16e29c90fa3e4bce to your computer and use it in GitHub Desktop.

[JPA] 즉시 로딩 (Eager), 지연 로딩 (Lazy)

❗️책 자바 ORM 표준 JPA 프로그래밍 공부한 내용을 정리한 글 입니다.

엔티티를 조회할 때 연관관계에 있는 엔티티를 조회하는 시점에 따라서 즉시 로딩 또는 지연 로딩으로 나눌 수 있다. 이름에서도 쉽게 유추할 수 있듯이 즉시 로딩은 엔티티를 조회할 때 연관되어 있는 엔티티와 함께 데이터베이스에서 조회하는 것을 의미하고, 지연 로딩은 대상 엔티티와 연관되어 있는 엔티티를 실제 사용 시점에 데이터베이스에서 조회하는 특징이 있다.

즉시 로딩 (Eager)

즉시 로딩은 @ManyToOnefetch 속성을 FetchType.EAGER 로 지정함으로써 설정할 수 있다. 예를 들어 Member 와 Team 엔티티가 있고, Member 가 Team 에 소속되는 관계 + 즉시 로딩으로 설정되어 있으면 Member 엔티티 조회 시점에 Member 엔티티와 연관관계를 맺고 있는 Team 엔티티까지 함께 데이터베이스로부터 조회힌다.

image [ 그림 1 ]

대부분의 JPA 구현체는 즉시 로딩을 최적화 하기 위해 가능하면 조인 쿼리를 사용한다. (자바 ORM 표준 JPA 프로그래밍 - 김영한 저)

조인 쿼리를 사용하여 두 대상을 함께 조회하는 것이다. 이 때 연관관계를 맺는 방식 (비즈니스 요구사항) 에 따라서 INNER JOINLEFT 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 에서는 @JoinColumnnullable 속성 값을 true 로 설정하면 된다. 그렇다면 ① 의 경우에는 INNER JOINLEFT OUTER JOIN 중 어떤 것을 사용할까?

image [ 그림 2 ]

[ 그림 2 ] 에서 각각의 쿼리 결과 조회 범위를 볼 수 있는데 ① 의 경우 INNER JOIN 을 사용 했을 때 의도한 결과가 나오지 않을 가능성이 있다.

image [ 그림 3 ]

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 에서는 @JoinColumnnullable 속성 값을 false 로 설정하면 된다. 무소속 상태의 Member 가 없다는 전제 이므로 보다 Member 엔티티 조회 시 INNER JOIN 을 사용해 Team 엔티티를 함께 조회 하더라도 무방하다.

지연 로딩 (Lazy)

프록시 객체를 사용하면 객체 탐색을 하는 시점에 연관관계에 있는 엔티티를 데이터베이스에서 조회할 수 있다.

JPA Proxy (프록시) 글에서 프록시를 위와 같이 설명 했다. 지연 로딩 전략에서 프록시 객체가 사용된다. 지연 로딩은 @ManyToOnefetch 속성을 FetchType.LAZY 로 지정 함으로써 설정할 수 있다.

image [ 그림 4 ]

SELECT * FROM member WHERE member_id = "member1" 

Member 엔티티를 조회 했을 때 지연 로딩 전략으로 연관관계 매핑을 했으므로 Team 엔티티는 데이터베이스에서 조회하지 않고 프록시 객체 형태로 생성한다. 그리고 team.getName() 등의 연관관계에 있는 엔티티를 실제 사용 (= 객체 그래프 탐색) 하는 시점에 프록시 객체 초기화가 진행되고, 아래 쿼리를 통해서 Team 엔티티를 조회한다.

SELECT * FROM team WHERE team_id = "team1" 

| Reference

자바 ORM 표준 JPA 프로그래밍 | 8장 프록시와 연관관계 정리

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment