JPA

지연로딩, 즉시로딩

91cm 2021. 8. 22. 21:56

Team과 Member의 관계가 1:N이고

member를 조회 할 때 member만 사용하는대 Team까지  가져오면 비용적 손해다.

그럴 경우 LAZY로딩을 이용해 proxy로 조회하는 방법을 쓰면된다.

 

ex) @ManyToOne(fetch = FetchType.LAZY) // 지연로딩

 

주의사항

1.기본은 모두 LAZY로딩으로 잡아 놓고, 필요한 부분만 EAGER로딩으로 변경하는 것을 권장하고 있다.

이유

  1-1 JPQL로 작성시 N+1 이슈발생.

  1-2 다른 팀원이 member만 조회하려고 findById를 날렸는대, team 쿼리까지나가면 당황. 

 

2. 연관관계 별로 fetch 기본값이 다름.

@ManyToOne -> EAGER

@OneToOne -> EAGER

@ManyToMany -> LAZY

@OneToMany -> LAZY  

 

 

@Entity
@Setter @Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "name"})
public class Team {

    @Id @GeneratedValue
    @Column(name = "team_id")
    private long id;

    private String name ;

    @OneToMany(mappedBy = "team")
    List<Member> members = new ArrayList<>();

    public Team(String name) {
        this.name = name;
    }
}
package study.datajap.entity;

import lombok.*;

import javax.persistence.*;

@Entity
@Setter @Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "username", "age"})
public class Member extends  BaseEntity{

    @Id @GeneratedValue
    private long id;

    private String username;

    private int age;

    @ManyToOne(fetch = FetchType.LAZY) // 지연로딩
    @JoinColumn(name = "team_id")
    private Team team;

    public Member(String username) {
        this.username = username;
    }

    public Member(String username, int age, Team team) {
        this.username = username;
        this.age = age;
        if(team != null){
            changeTeam(team);
        }
    }

    public Member(String username, int age) {
        this.username = username;
        this.age = age;
    }

    public void changeTeam(Team team){
        this.team = team ;
        team.getMembers().add(this);
    }
}
@Test
public void 지연로딩 () {
    Optional<Member> byId = memberRepository.findById(2L); // fetch = FetchType.EAGER OR LAZY
    // byId.get().getTeam() // 호출 시점에 Team 쿼리 날림
}
Hibernate: 
    select
            member0_.id as id1_1_,
            member0_.reg_date as reg_date2_1_,
            member0_.update_date as update_d3_1_,
            member0_.reg_user_id as reg_user4_1_,
            member0_.update_user_id as update_u5_1_,
            member0_.age as age6_1_,
            member0_.team_id as team_id8_1_,
            member0_.username as username7_1_ 
        from
            member member0_ 
        where
            member0_.username=?

 

즉시로딩으로 변경할 경우 join된 쿼리가 날라가게 된다.

    @ManyToOne(fetch = FetchType.EAGER) // 즉시로딩
    @JoinColumn(name = "team_id")
    private Team team;

    public Member(String username) {
        this.username = username;
    }
Hibernate: 
    select
        member0_.id as id1_1_0_,
        member0_.reg_date as reg_date2_1_0_,
        member0_.update_date as update_d3_1_0_,
        member0_.reg_user_id as reg_user4_1_0_,
        member0_.update_user_id as update_u5_1_0_,
        member0_.age as age6_1_0_,
        member0_.team_id as team_id8_1_0_,
        member0_.username as username7_1_0_,
        team1_.team_id as team_id1_2_1_,
        team1_.name as name2_2_1_ 
    from
        member member0_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id 
    where
        member0_.id=?

 

 

그러면 모든 연관관계를 LAZY로딩으로 바꾸었다가, 특정한 곳에서 member,team을 동시에 조회 하는 경우가 생긴다면???

-> JPQL의 패치 조인을 사용하거나 , @Entitygraph를 사용하면 된다!