Updated:

1. 개요

JPA는 즉시 로딩과 지연 로딩을 통해 연관된 데이터를 함께 조회해 올지, 아니면 연관된 데이터는 사용하는 시점에 불러올지 정할 수 있다. 이번에는 즉시 로딩과 지연 로딩에 대해 알아보도록 하자.

2. 즉시 로딩 (EAGER)

연관된 데이터를 함께 조회해오는 것을 즉시 로딩(EAGER)이라고 한다. @ManyToOne, @OneToOne의 기본 값이 EAGER로 되어있는데, 즉시 로딩은 N+1 문제를 발생시킬 수 있으므로 가급적 지연 로딩을 사용해야 한다.

2-1. 예제 코드

[Team.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
@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

[Member.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
29
30
31
32
33
34
35
36
37
38
@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Team getTeam() {
        return team;
    }

    public void setTeam(Team team) {
        this.team = team;
    }
}

Line 11 : @ManyToOne은 fetch 타입을 지정하지 않으면 default 값은 즉시 로딩(EAGER)

[JpaMain.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
29
30
31
32
33
34
35
36
37
38
39
40
public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Team team = new Team();
            team.setName("teamA");
            em.persist(team);

            Member member1 = new Member();
            member1.setUsername("member1");
            member1.setTeam(team);
            em.persist(member1);

            em.flush();
            em.clear();

            Member m = em.find(Member.class, member1.getId());
            System.out.println("m = " + m.getTeam().getClass());

            System.out.println("==================");
            System.out.println("teamName = " + m.getTeam().getName());
            System.out.println("==================");

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

Line 23 : fetch 타입이 EAGER이므로 join을 통해 연관된 데이터 함께 조회

Line 24 : join으로 team 엔티티를 얻어왔으므로 team 클래스 출력

Line 27 : 앞으로 join으로 연관된 데이터를 함께 조회해왔으므로 추가쿼리 없이 값 출력

2-2. 실행 결과

3. 지연 로딩 (LAZY)

프록시를 통해 실제로 사용하는 시점에 연관된 데이터를 조회해오는 것을 지연 로딩(LAZY)이라고 한다.

3-1. 예제 코드

[Team.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
@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

[Member.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
29
30
31
32
33
34
35
36
37
38
@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Team getTeam() {
        return team;
    }

    public void setTeam(Team team) {
        this.team = team;
    }
}

[JpaMain.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
29
30
31
32
33
34
35
36
37
38
39
40
public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Team team = new Team();
            team.setName("teamA");
            em.persist(team);

            Member member1 = new Member();
            member1.setUsername("member1");
            member1.setTeam(team);
            em.persist(member1);

            em.flush();
            em.clear();

            Member m = em.find(Member.class, member1.getId());
            System.out.println("m = " + m.getTeam().getClass());

            System.out.println("==================");
            System.out.println("teamName = " + m.getTeam().getName());
            System.out.println("==================");

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

Line 23 : fetch 타입이 LAZY이므로 member만 조회

Line 24 : 현재 team은 프록시 객체임을 확인

Line 27 : 실제로 사용하는 시점에 team 테이블 조회 후 값 출력

3-2. 실행 결과

출처 : 자바 ORM 표준 JPA 프로그래밍 - 기본편

Updated:

Leave a comment