JPA

[JPA] 15. 즉시 로딩과 지연 로딩

inuma 2021. 1. 18. 21:42
프로젝트 git url github.com/rtef23/JpaStudy
필요 1. docker
2. docker-compose
프로젝트 정보 mysql : 5.7 - docker image
java : 1.8

 

지연 로딩

  • 연관 관계에 있는 엔티티를 데이터베이스에서 조회할 때 같이 조회하는 것이 아니라, 연관 관계에 있는 엔티티를 사용하는 경우 추가 쿼리로 데이터베이스에서 조회하도록 하는 방법
  • 연관된 엔티티를 fetch = FetchType.LAZY로 설정하면 된다.
  • 최초 조회시에 연관 관계의 엔티티는 hibernate 프록시 객체로 들어가 있게 된다.
  • @OneToMany, @ManyToMany (xxxToMany)는 지연 로딩이 기본 값이다.
  • 가급적 지연 로딩을 사용하는 것을 추천한다.
@Entity
public class Member {
  @Id
  @GeneratedValue
  @Column(name = "member_id")
  private Long id;

  private String name;

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

@Entity
public class Team {
  @Id
  @GeneratedValue
  @Column(name = "team_id")
  private Long id;

  private String name;

  @OneToMany(mappedBy = "team")
  private List<Member> members;
  
  ...
}

 

  • 조회된 데이터의 리스트를 돌면서 N + 1 문제가 동일하게 발생할 수 있지만, fetch join과 같은 방법을 이용하여 해결할 수 있다.
List<Member> findMembers = entityManager.createQuery("select m from Member m join fetch m.team", Member.class).getResultList();

 

  • 아래의 코드에서 Team 엔티티를 조회하는 시점은 findMember1.getTeam().getName()을 호출한 시점에서 데이터베이스에서 조회한다.(getTeam을 호출한 시점이 아닌 getName을 호출한 시점)
Member findMember1 = entityManager.find(Member.class, member1.getId());

System.out.println("#### founded member name : " + findMember1.getName());
System.out.println("#### founded team name : " + findMember1.getTeam().getName());

 

즉시 로딩

  • 연관관계에 있는 엔티티를 최초 조회시 조인하여 함께 조회하는 방법
  • 연관된 엔티티를 fetch = FetchType.EAGER 로 설정하면 된다.
  • 즉시 로딩은 예기치 않은 쿼리를 생성할 수 있기 때문에 가능한 지양하는 편이 좋다.
  • JPQL에서 N + 1 문제를 일으킬 수 있다.
Team team = new Team();
team.setName("test-team-1");

entityManager.persist(team);

Team team2 = new Team();
team2.setName("test-team-2");

entityManager.persist(team2);

Member member1 = new Member();
member1.setName("test-member-1");
member1.setTeam(team);

entityManager.persist(member1);

Member member2 = new Member();
member2.setName("test-member-2");
member2.setTeam(team);

entityManager.persist(member2);

Member member3 = new Member();
member3.setName("test-member-3");
member3.setTeam(team2);

entityManager.persist(member3);

entityManager.flush();
entityManager.clear();

List<Member> findMembers = entityManager.createQuery("select m from Member m", Member.class).getResultList();
//서로 다른 team인 team, team2를 조회하기 위해서 2번의 team 테이블을 조회하는 쿼리가 실행되게 된다.

  • @ManyToOne, @OneToOne은 (xxxToOne) 즉시 로딩이 기본값이다.
@Entity
public class Member {
  @Id
  @GeneratedValue
  @Column(name = "member_id")
  private Long id;

  private String name;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "team_id")
  private Team team;
  
  ...
}

@Entity
public class Team {
  @Id
  @GeneratedValue
  @Column(name = "team_id")
  private Long id;

  private String name;

  @OneToMany(mappedBy = "team")
  private List<Member> members;
  
  ...
}

 

  • 아래 코드에서 Team은 Member 조회시 함께 조회하게 된다.
Member findMember1 = entityManager.find(Member.class, member1.getId());

System.out.println("#### founded member name : " + findMember1.getName());
System.out.println("#### founded team name : " + findMember1.getTeam().getName());

 

 

'JPA' 카테고리의 다른 글

[JPA] 17. 값 타입  (0) 2021.01.21
[JPA] 16. 영속성 전이(CASCADE)  (0) 2021.01.18
[JPA] 14. Hibernate 프록시  (0) 2021.01.15
[JPA] 13. @MappedSuperclass  (0) 2021.01.11
[JPA] 12. 상속 관계 매핑  (0) 2021.01.11