프록시의 개념을 미리 알아야 이해 할 수 있다.
지연로딩
FK에 @ManyToOne(fetch = FetchType.LAZY) 어노테이션에 지연로딩을 사용
@Entity
public class Member{
@ID @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
}
Member m = em.find(Member.class, member.getId()); // Team은 가져오지 않고 Member 객체만 가져올 수 있음
System.out.println("m = " + m.getTeam().getClass()); // Team은 프록시 객체로 가져옴
m.getTeam().getName(); // 프록시 객체 초기화
즉 Member 객체를 조회할땐 Member 객체의 필드값만 가져오게 되고, 연관된 Team은 프록시 객체로만 가져온다. 그후 Team을 실제 사용하는 시점에 초기화 하여 값을 가져오게 된다.
주로 Member테이블과 Team 테이블이 서로 같이 사용되지 않을때 지연로딩을 사용한다.
반대로 Member와 Team이 서로 자주 사용될때 지연로딩을 사용한다면 Member 조회따로 Team조회 따로하며 DB를 자주 조회하게 되므로 성능상 좋지 않다. 따라서 즉시 로딩을 사용한다.
즉시로딩
@ManyToOne(fetch = FetchType.EAGER) 어노테이션에 즉시로딩을 사용
@Entity
public class Member{
@ID @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
}
em.find()를 사용할때 프록시를 사용하지않고, Team과 Member를 다 가져오게 된다.
실제 실무에서는 지연로딩을 사용하는게 좋다. 즉시로딩은 Team과 Member를 전부 Join해서 반환하게 되는데 엔티티의 갯수가 많아지면 전부다 Join을 하고, 모든 값들을 가져오기 때문에 쿼리가 DB에 매우 많이 보내지게 된다. 즉 성능 이슈가 있기 때문에 지연로딩을 권장한다.
또한 즉시로딩을 통해 JPQL을 사용한다면 N+1 문제가 생기게 된다.
List<Member> member = em.createQuery("select m from Member m", Member.class)
.getResultList();
이렇게 하면 SELECT쿼리를 DB에 보내 Member를 가져오게 된다. 그후 Member를 보니 Team도 가져와야해서 Team을 가져오는 쿼리문을 DB에 보내고 Team을 가져오게 된다. 예를들어 Team 인스턴스가 여러개 있다면, Member를 가져오고 Team1, Team2....을 DB에서 여러번 가져와야하는 이슈가 발생하는 것이다.
@ManyToOne, @OneToOne 은 기본이 즉시로딩이므로 반드시 LAZY로 설정하기를 권장한다. (@OneToMany, @ManyToMany는 기본이 지연로딩)
'스프링' 카테고리의 다른 글
[스프링][자바]Reflection API (0) | 2023.02.15 |
---|---|
[스프링] DI, IoC, Bean (0) | 2023.02.13 |
[스프링] 프록시란 (0) | 2023.02.10 |
[스프링] 기본키 관련 매핑 Annotation (0) | 2023.02.09 |
[스프링] 필드와 컬럼 매핑 Annotation (0) | 2023.02.09 |