영속성 컨텍스트의 주요 기능인 지연 로딩에 대해 정리하고자 한다.
JPA에서는 테이블 간의 관계를 엔티티에 표현할 수 있고,
하나의 테이블을 조회할 때 연결된 다른 테이블을 함께 조회할 수 있다.
회원 테이블과 팀 테이블이 있을 때, 하나의 팀에는 여러 회원이 있을 수 있으므로 다대일의 관계를 가지고 있다.
두 테이블의 관계를 엔티티에서 아래 코드와 같이 표현할 수 있다.
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private Long id;
private String username;
@ManyToOne(fetch = FetchType.EAGER) // Member 기준으로 다대일이므로 @ManyToOne이다.
@JoinColumn(name = "TEAM_ID")// @JoinColumn은 외래키를 가지는 곳에 사용한다.
private Team team;
}
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team") // Team 기준으로 일대다이므로 @OneToMany이다.
private List<Member> members = new ArrayList<>();
}
이때 회원을 조회할 때 팀 정보를 함께 가져올 수 있고, 팀을 조회할 때 팀에 속한 회원들의 정보를 함께 가져올 수 있다.
@ManyToOne의 fetch 속성을 FetchType.EAGER로 설정하면 회원을 조회할 때 팀 정보를 함께 가져올 수 있다.
이때, 최적화하기 위해 조인 쿼리를 사용한다.
여기서 FetchType.EAGER는 '즉시 로딩'이라 한다. 그럼 '지연 로딩'은 무엇일까?
지연 로딩이란 회원을 조회할 때 팀을 조회하지 않으며, team 데이터가 필요해 접근할 때 조회한다.
Member member = em.find(Member.class, "member1"); // 회원만 조회
Team team = member.getTeam(); // team은 프록시 객체
team.getName(); // 이때, 팀 조회
그럼 team 데이터에 접근하기 전에는 team 필드에는 어떤 값이 들어있을까?
바로 가짜 클래스인 '프록시 객체' 가 저장된다.
프록시 객체는 실제 클래스를 상속 받아 만들어진 것으로, 실제 객체에 대한 참조를 보관한다.
이때 DB를 조회하기 이전이므로 실제 객체에 대한 참조값은 null이다.
team에 접근하게 되면, 영속성 컨텍스트에 실제 엔티티가 있는지 확인하고
없다면, 영속성 컨텍스트가 DB를 조회해서 실제 엔티티 객체를 생성하고 참조값을 프록시 객체에 저장한다.
있다면, 프록시가 아닌 실제 엔티티를 반환한다.
여기서 주의할 점은 영속성 컨텍스트에 없을 때는 프록시 객체를 반환하지만, 있을 때는 실제 엔티티를 반환한다는 것이다. 또한, 프록시 객체 초기화는 영속성 컨텍스트가 해주므로 team은 영속 상태여야 한다.