[JPA] 프록시와 즉시로딩, 지연로딩

홍건의·2022년 4월 6일
0

JPA 학습

목록 보기
8/11

프록시가 필요한 이유

아래 두 Entity가 정의되었다고 가정하자.

@Entity
public class Member {
	
    private String username;
    
    @ManyToOne
    private Team team;
    
    public Team getTeam() {
    	return team;
    }
    
    public String getUsername() {
    	return username;
    }
}
@Entity
public class Team {
	
    private String name;
    
    public String getName() {
    	return name;
}

아래와 같은 코드를 실행한다고 했을 때,

sout(member.getUsername());
sout(team.getName());

문제가 team.getName()에서 나타난다.
team.getName()이 없다면?
member를 가져왔는데... team을 안 쓸 거라면?
굳이 Member.team를 DB에서 같이 조회하는 것은 효율적이지 않다

그래서 JPA에서는 이런 문제를 해결하려고 Entity가 실제 사용될 때까지 DB 조회 지연하는 방법을 제공한다. 이러한 메커니즘을 지연로딩이라고 한다.

필요한 순간에 DB에서 team 정보를 가져오는 것이다. 예를 들면 team.getName()이 호출되는 순간에 정보를 가져온다.

그런데 문제는 이런 지연로딩 기능을 사용하려면 실제 Entity 대신에 DB 조회를 지연할 수 있는 가짜 객체가 필요한데 이것을 프록시 객체라고 한다.

프록시 개념 설명

JPA에서 실제 Entity를 조회할 때는

 User user = em.find(User.class, "1L");

이러한 코드로 DB에서 읽어온다.
그런데 지연하고 싶으면

  User user = em.getReference(User.class, "1L");

이 메소드를 호출하여 JPA는 실제 DB를 조회하지 않고, Entity객체도 생성하지 않는다. 대신에 DB 접근을 위임한 프록시 객체를 반환한다.

프록시 객체

프록시 클래스는 실제 클래스를 상속 받는다.

public class Proxy_User extends User {
	
    private User user;
    
    public Long getId();

	public String userName();
       
}

프록시 사용 시 특징

  • 프록시 객체는 처음 사용할 때 한번만 초기화된다.
  • 원본 엔티티를 상속받았으므로 타입 체크 시 주의해서 사용해야 한다.
  • 영속성 컨텍스트에 찾는 엔티티가 있으면 DB를 조회하지 않고 em.getReferenece()를 호출했을 때 실제 엔티티를 반환한다.
  • 초기화는 영속성 컨텍스트의 도움을 받아야 가능하다. 준영속에서 초기화하면 LazyInitializaionException을 발생시킨다.

프록시 초기화 여부 확인

PersistenceUnitUtil.isLoaded(Object Entity)를 사용하면 인스턴스의 초기화 여부를 확인할 수 있다.
초기화되었거나 프록시 인스턴스가 아니면 true를 반환한다.
초기화되지 않은 프록시일 때만 false를 반환한다.

디폴트 설정값

@xxToOne : FetchType.EAGER
@xxToMany : FetchType.LAZY

가져오는 방법

(1) 즉시로딩
대부분의 JPA 구현체는 조인쿼리를 사용하여 가져온다.

즉시 로딩

@JoinColumn(nullable = true) : Left Outer Join 사용
@JoinColumn(nullable = false) : Inner Join 사용

외래키로 가져올 때 대상이 존재하지 않을 수 있으므로 기본은 Left Outer Join을 통해 가져온다.

(2) 지연로딩
쿼리를 두 번 날린다.

SELECT * FROM MEMBER WHERE MEMBER_ID = ?
SELECT * FROM TEAM WHERE TEAM_ID = ?

즉시로딩 사용 시 주의사항

(1) 컬렉션을 여러 개 즉시로딩(일대다 즉시로딩)하는 것은 성능 저하를 초래할 수 있다. 조인을 하는데 여러 테이블과 조인해야 하므로 많은 연산이 필요해진다.

(2) 일대다 즉시 로딩Outer Join을 사용해야 한다. 예를 들어 회원 - 팀이라고 가정했을 때, 팀에서 회원들을 조회한다면 한명도 없는 팀의 경우 조회 결과가 나타나지 않을 수 있기 때문이다. 일대다 즉, Entity 클래스 내부에 컬렉션을 사용하는 경우는 Outer Join을 사용하는 것으로 기억하자.

정리

지연로딩 : 연관된 엔티티를 proxy로 조회하고, 실제 사용되어질 때 초기화하고 DB에서 조회한다.
즉시로딩 : 연관된 엔터티를 즉시 조회한다. Hibernate는 가능하면 SQL 조인을 사용하여 한 번에 조회한다.

profile
Backend Developer

0개의 댓글

관련 채용 정보