JPA 프록시와 지연로딩

프록시

EntityManager에는 getReference()라는 메서드가 있다.

find()와 같이 객체를 조회하는 메서드이지만, getReference()는 반환 타입이 Proxy이다.

Proxy는 보통 전달하는 역할을 할 때 자주 등장하는 단어인데 여기서도 같은 의미로 사용된다.

Proxy 객체는 실제 객체와 같은 모양에 Id 값만 들어있는 빈 객체이다.

 

프록시 객체를 사용하는 이유 

연관관계 매핑에서 객체를 조회하면 연관된 객체도 같이 조회하게 된다. 이때 연관된 객체는 사용하지 않더라도 같이 조회되는 문제가 있다.

이런 문제를 해결하기 위해서 실제 연관객체를 사용하는 시점에 쿼리를 보내 데이터를 넣어주게되고, 연관 객체를 가져오기 전까지는 null이 아닌 빈 껍데기 객체를 넣어두어 NPE도 방지하고, 프록시 객체를 통해서 쿼리를 요청할 수있게 된다.

 

객체 사용 시점이란 Member에서 Team을 단순히 가져온 것이 아닌 사용할 때로 member.getTeam()을 호출 했을 때는 쿼리를 요청하지 않고, member.getTeam().setName("TEAM_A")을 호출했을 때 쿼리가 발생한다. 

 

프록시 객체는 처음 사용할 때 한번만 초기화 되고, 초기화가 될 때 1차 캐시에 저장되기 때문에 이후부터는 조회할 때 쿼리가 발생하지 않으며, 프록시 객체는 엔티티를 상속받기 때문에 초기화 후에도 여전히 Proxy 타입이다.

 

getReference() 호출 시 무조건 프록시 객체를 반환하는 것은 아니고 1차 캐시에서 먼저 조회 후 존재한다면 바로 객체를 반환한다.

또한, Proxy 객체를 초기화 하는 시점에 객체가 영속 상태가 아니라면 could not initialize proxy 에러가 발생한다. 

ex) DTO에 Entity 필드를 넣으면 영속성 컨텍스트를 벗어난 상태에서 프록시 객체를 초기화하는 일이 발생할 수 있다.

 

프록시 객체 동작 과정

 

 

 

 

지연 로딩

연관 관계를 가진 Member와 Team 엔티티를 조회할 때 Member와 Team이 둘 다 필요한 경우가 있고, Member만 필요한 경우가 있다.

이때 지연 로딩을 통해서 Team에 대한 쿼리 없이 Member만 가져오는 것이 가능하다.

지연 로딩을 사용하면 프록시를 이용해서 아래와 같이 Member와 Team을 필요한 시점에 따라 따로 조회하게 된다.

아래 예시에서 총 2번의 select 쿼리가 발생하게 된다. 하지만 member만 사용한다면 1번의 쿼리만 발생하게 된다.

@ManyToOne(fetch = FetchType.LAZY)

 

 

즉시 로딩

즉시 로딩은 지연 로딩과 반대로 처음 조회할 때 조인을 통해 연관된 객체를 모두 가져오는 방법이다.

Team과 Member가 항상 같이 사용된다면 즉시 로딩을 사용할 수도 있다.

 

하지만 즉시 로딩은 조인 테이블이 많아질수록 예상치 못한 추가 쿼리가 발생할 수 있다는 문제가 있다.

실무에서는 즉시 로딩을 사용하지 않는 것을 권장하고, 한번에 조인해서 가져와야하는 경우 JPQL을 사용하는 것이 좋다.

기본 값이 즉시 로딩이기 때문에 LAZY로 명시해주어야한다.

@ManyToOne(fetch = FetchType.EAGER)

 

 

 

N + 1 문제

하나의 쿼리로 인해 N개의 추가 쿼리가 발생하는 문제를 말한다.

 

즉시 로딩에서의 N+1 문제

JPQL 사용 시 "select t  from Team t"를 통해 Team만 조회해도 즉시 로딩에 의해 Team의 Member를 전부 조회하게 된다.

 

지연 로딩에서의 N+1 문제

N개의 엔티티를 사용할 때 문제가 발생할 수 있다.

예를 들어 하나의 Member를 조회했을 때는 Team이 사용될 때 한번의 select 쿼리만 추가된다.

하지만, Member List를 받았을 때 N개의 Member가 전부 team을 사용한다면? N번의 추가 쿼리가 발생하게 된다.

 

 

'개발 > Spring' 카테고리의 다른 글

JPA 쿼리 방법  (0) 2024.01.28
JPA 값 타입  (0) 2024.01.28
JPA 영속성 전이와 고아 객체  (0) 2024.01.28
JPA 엔티티 매핑  (1) 2024.01.27
JPA 동작 과정  (0) 2024.01.26