Skip to content

Instantly share code, notes, and snippets.

Revisions

  1. taekwon-dev revised this gist Jun 4, 2022. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions [JPA] 즉시 로딩 (Eager), 지연 로딩 (Lazy).md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,7 @@
    # [JPA] 즉시 로딩 (Eager), 지연 로딩 (Lazy)

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

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


  2. taekwon-dev created this gist Jun 4, 2022.
    85 changes: 85 additions & 0 deletions [JPA] 즉시 로딩 (Eager), 지연 로딩 (Lazy).md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,85 @@
    # [JPA] 즉시 로딩 (Eager), 지연 로딩 (Lazy)
    엔티티를 조회할 때 `연관관계`에 있는 `엔티티를 조회하는 시점`에 따라서 즉시 로딩 또는 지연 로딩으로 나눌 수 있다. 이름에서도 쉽게 유추할 수 있듯이 즉시 로딩은 엔티티를 조회할 때 연관되어 있는 엔티티와 함께 데이터베이스에서 조회하는 것을 의미하고, 지연 로딩은 대상 엔티티와 연관되어 있는 엔티티를 실제 사용 시점에 데이터베이스에서 조회하는 특징이 있다.


    ### 즉시 로딩 (Eager)

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

    ![image](https://user-images.githubusercontent.com/70354365/172015012-953c56dc-d508-4009-9389-25b575b2da1c.png)
    [ 그림 1 ]


    > 대부분의 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` 중 어떤 것을 사용할까?

    ![image](https://user-images.githubusercontent.com/70354365/172015089-161e3d14-59b0-49f5-8013-96dc193afea9.png)
    [ 그림 2 ]

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

    ![image](https://user-images.githubusercontent.com/70354365/172015132-f37a23b3-23bc-4280-86e4-8873d29bd500.png)
    [ 그림 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 에서는 `@JoinColumn``nullable` 속성 값을 `false` 로 설정하면 된다. 무소속 상태의 Member 가 없다는 전제 이므로 보다 Member 엔티티 조회 시 `INNER JOIN` 을 사용해 Team 엔티티를 함께 조회 하더라도 무방하다.

    ### 지연 로딩 (Lazy)

    > 프록시 객체를 사용하면 객체 탐색을 하는 시점에 연관관계에 있는 엔티티를 데이터베이스에서 조회할 수 있다.
    [JPA Proxy (프록시)](https://medium.com/taekwon-v/jpa-proxy-%ED%94%84%EB%A1%9D%EC%8B%9C-c3b2402abdc7) 글에서 프록시를 위와 같이 설명 했다. 지연 로딩 전략에서 프록시 객체가 사용된다. 지연 로딩은 `@ManyToOne``fetch` 속성을 `FetchType.LAZY` 로 지정 함으로써 설정할 수 있다.

    ![image](https://user-images.githubusercontent.com/70354365/172015150-d7343234-755b-4b34-9d2d-1c33a870950b.png)
    [ 그림 4 ]

    ```
    SELECT * FROM member WHERE member_id = "member1"
    ```

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

    ```
    SELECT * FROM team WHERE team_id = "team1"
    ```


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