[Spring] JPA 프록시

신범철·2023년 10월 5일
0

스프링부트

목록 보기
15/20

프록시의 등장 배경

  • Member 엔티티를 조회할 때 Team 엔티티도 함께 조회해야 할까?라는 질문으로 시작하자
    • 정확한 정답은 실제 비즈니스 로직에 따라 다르다.
    • 비즈니스 로직에서 Team이 필요하지 않는 경우에 항상 Team을 가져오는 경우 낭비가 발생한다.
    • JPA는 이를 낭비하지 않기 위해, 지연로딩, 프록시라는 개념으로 해결한다.

프록시란?

  • 사전적 의미 : 대리(행위)나 대리인을 의미한다.
    -> 어떤 것을 대신해주는 것이다.

  • 객체를 조회할 때 마다 내부 참조 객체에 대한 모든 정보를 가져올 필요는 없다.
    -> 프록시 객체를 가져와 필요할 때 진짜를 가져오는 방식으로 구현한다.

em.find() vs em.getReference()

  • em.find() : DB를 통해 실제 엔티티 객체 조회
  • em.getReference() : DB조회를 미루고 가짜(프록시) 엔티티 조회

프록시 객체

실제 엔티티를 가리키는 target이라는 값이 들어있다.(최초의 target은 null값이다.)
이후 실제 객체에 접근하는 .getMember()를 호출하면 실제 DB에 접근해 target에 실제 엔티티가 연결된다.

프록시 객체의 동작 원리

  1. em.getReference()로 호출하여 프록시 객체가 반환된다.
    (target 안에 null이 들어있음)
  2. 실제 객체에 접근하는 .getMember() 호출
  3. target이 null이기 때문에 영속성 컨텍스트에 조회 요청
  4. 영속성 컨텍스트는 실제 DB에서 조회한 후 target에 실제 엔티티를 연결한다.(프록시 객체 초기화)
  5. 프록시 객체는 target을 통해 멤버를 호출한다.
  • 프록시 객체 초기화란?
    • target이 실제 엔티티와 연결되는 과정을 말한다.
    • 프록시 객체 초기화는 최초 1회만 수행한다.

프록시 확인 메서드들

  • emf.getPersistenceUnitUtil().isLoaded(Object object)
    • 프록시 인스턴스의 초기화 여부를 확인
  • entity.getClass().getName()
    • 프록시 클래스를 확인한다.
  • org.hibernate.Hibernate.initialize(entity);
    • 프록시 초기화를 강제로 한다.
    • 이때 select 쿼리가 나가 엔티티를 연결한다.

프록시 특징 & 주의점

  • 프록시 객체 초기화는 최소 사용시 처음에만 수행한다.
  • 프록시 객체 초기화시 프록시 객체가 실제 엔티티로 바뀌는 것이 아니라 target이 실제 엔티티를 가리키는 것이다.
  • 프록시 객체는 원본 엔티티를 상속받기 때문에 타입체크에 유의해야 한다.
    • em.find().getClass() == em.getReference.getClass()//X
    • em.find().getClass() instanceof Member//O
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면, em.getReference()해도 실제 엔티티가 변환된다.
    • JPA는 동일 객체임을 보장하기 위해 프록시를 먼저 조회하면 프록시 객체로 맞추고, 아니면 실제 엔티티에 맞추는 내부 로직이 있다.
    • 그렇기 때문에 JPA는 하나의 트랜잭션에서 같은 객체의 조회는 항상 동일 객체를 보장한다.
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태에서 프록시 초기화를 하면 오류 발생
    • 여기서 중요한 것은 프록시가 영속성 컨텍스트를 통해서 일어난다는 것을 알고 넘어가자
    • LazyInitializationException 발생
profile
https://github.com/beombu

0개의 댓글