흰 스타렉스에서 내가 내리지

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

Spring

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

주씨. 2023. 2. 19. 13:16
728x90

엔티티를 조회할 때 사용하지 않는 연관된 엔티티까지 데이터베이스에서 함께 조회해 두는 것은 효율적이지 않다.

JPA는 이런 문제를 해결하려고 엔티티가 실제 사용될 때까지 데이터베이스 조회를 지연하는 방법을 제공하는데 이것을 지연로딩이라고 한다.

team.getName() 처럼 팀 엔티티 값을 실제 사용하는 시점에 데이터베이스에서 팀 엔티티에 필요한 데이터를 조회하는 것이다. 

 

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

 

Member member = em.find(Member.class, "member1");

이렇게 엔티티를 직접 조회하면 조회한 엔티티를 실제 사용하든 사용하지 않든 데이터베이스를 조회하게 된다.

엔티티를 실제 사용하는 시점까지 데이터베이스 조회를 미루고 싶으면 EntityManager.getReference() 메소드를 사용하면 된다.

Member member = em.getReference(Member.class, "member1");

이 메소드를 호출할 때 JPA는 데이터베이스를 조회하지 않고 실제 엔티티 객체도 생성하지 않는다.

대신에 데이터베이스 접근을 위임한 프록시 객체를 반환한다.

 

프록시

프록시 클래스는 실제 클래스를 상속 받아서 만들어지므로 실제 클래스와 겉 모양이 같다.

프록시 객체는 실제 객체에 대한 참조(target)를 보관한다. 

그리고 프록시 객체의 메소드를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.

 

// 프록시 클래스 예상 코드
public class MemberProxy extends Member{
    Member target = null; // 실제 엔티티 참조
    public String getName(){
        if(target == null){
            // 2. 초기화 요청
            // 3. DB 조회
            // 4. 실제 엔티티 생성 및 참조 보관
            this.target = ...;
        }
        // 5. target.getName();
        return target.getName();
    }
}

 

영속성 컨텍스트에 찾는 엔티티가 이미 있으면 데이터베이스를 조회할 필요가 없으므로 em.getReference() 를 호출해도 프록시가 아닌 실제 엔티티를 반환한다.

 

 

프록시 객체는 식별자 값을 보관한다.

Team team = em.getReference(Team.class, "team1");  //식별자 보관
team.getId();  //초기화되지 않음

@Access(AccessType.PROPERTY) 로 설정한 경우에는 team.getId() 를 호출해도 프록시를 초기화하지 않는다.

@Access(AccessType.FIELD) 로 설정한 경우에는 team.getId() 를 호출해도 프록시 객체를 초기화한다.

 

프록시 객체는 주로 연관된 엔티티를 지연 로딩할 때 사용한다.

 

즉시로딩과 지연로딩

즉시로딩 : 엔티티를 조회할 때 연관된 엔티티도 함께 조회한다.

@ManyToOne(fetch = FetchType.EAGER)

 

지연로딩 : 연관된 엔티티를 실제 사용할 때 조회한다.

@ManyToOne(fetch = FetchType.LAZY)

 

 

fetch 속성의 기본 설정값은 다음과 같다.

- @ManyToOne, @OneToOne : 즉시로딩 (FetchType.EAGER)

- @OneToMany, @ManyToMany : 지연로딩 (FetchType.LAZY)

 

JPA의 기본 fetch 전략은 연관된 엔티티가 하나면 즉시 로딩을, 컬렉션이면 지연 로딩을 사용한다.

추천하는 방법은 모든 연관관계에 지연 로딩을 사용하는 것이다. 그리고 애플리케이션 개발이 어느 정도 완료단계에 왔을 때 실제 사용하는 상황을 보고 꼭 필요한 곳에만 즉시 로딩을 사용하도록 최적화하면 된다.