JPA

[JPA] 14. Hibernate 프록시

inuma 2021. 1. 15. 19:00
프로젝트 git url github.com/rtef23/JpaStudy
필요 1. docker
2. docker-compose
프로젝트 정보 mysql : 5.7 - docker image
java : 1.8

Hibernate 프록시

사용 이유

  • 조회시 연관관계에 있는 데이터를 사용하는 경우에만 추가로 조회하도록 하는 방법(지연로딩, 즉시로딩)

특징

  • 실제 클래스를 상속 받아서 생성됨
    • 동일성 비교 결과 값은 false이다.
      • 아래 예제에서는 영속성 컨텍스트에서 객체를 가져오지 않는 경우를 가정
    • 타입 비교시에는 반드시 instanceof 연산자로 비교해야한다.
Member memberEntity = entityManager.find(Member.class, 1L);
Member memberProxy = entityManager.getReference(Member.class, 1L);

memberEntity == memberProxy //false

memberEntity instanceof Member //true
memberProxy instanceof Member //true
  • 실제 클래스와 겉 모양이 같음
  • 사용하는 입장에서는 프록시 객체인지 실제 엔티티 객체인지 구분하지 않고 사용해도 괜찮음(이론상)
  • 실제 객체의 참조(target)을 보관
  • 프록시 객체를 호출하면 프록시 객체는 실제 엔티티 객체의 메소드를 호출

  • 처음 사용할 때 한번만 초기화
  • 프록시 객체는 초기화시 실제 엔티티로 변경되는 것이 아니라 프록시 객체를 통해서 실제 엔티티를 접근 가능해지는 것!!!
  • 영속성 컨텍스트에 찾고자하는 엔티티가 있는 경우, 프록시 객체를 리턴하는 것이 아니라 실제 엔티티를 반환하게된다.
    • 먼저 영속성 컨텍스트에 등록된 프록시 객체 또는 실제 엔티티를 반환하도록 되어 있는듯 하다.
Member memberEntity = entityManager.find(Member.class, 1L);
//식별자가 1L인 member entity는 영속성 컨텍스트에 등록되게 됨
Member memberReference = entityManager.getReference(Member.class, 1L);


memberEntity == memberReference //true
Member memberReference1 = entityManager.getReference(Member.class, 1L);
Member memberReference2 = entityManager.getReference(Member.class, 1L);

memberReference1 == memberReference2 //true
Member memberReference = entityManager.getReference(Member.class, 1L); //Proxy
Member memberEntity = entityManager.find(Member.class, 1L); //Entity
//실제 엔티티가 리턴되는 것이 아니라 member 프록시 객체가 리턴되게 된다.

memberReference == memberEntity //true
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 예외가 발생하게 된다.
Member memberRef = entityManager.getReference(Member.class, 1L);

entityManager.detach(memberRef); //entityManager.clear()도 동일

memberRef.getName();
/*
  org.hibernate.LazyInitializationException 예외 발생
*/

 

entityManager.find() VS entityManager.getReference()

entityManager.find()

  • 데이터 베이스를 통해 실제 엔티티를 조회
Member findMember1 = entityManager.find(Member.class, member1.getId());

System.out.println("#### START ####");
System.out.println(findMember1.getClass());

System.out.println(findMember1.getId());
System.out.println(findMember1.getName());
System.out.println("#### END ####");

  • #### START ####와 #### END #### 사이에 쿼리 조회가 없다.
  • 조회된 객체의 클래스는 해당 엔티티의 클래스이다.

 

entityManager.getReference()

  • 데이터 베이스 조회를 미루는 프록시 엔티티 객체 조회
Member findMember1 = entityManager.getReference(Member.class, member1.getId());

System.out.println("#### START ####");
System.out.println(findMember1.getClass());

System.out.println(findMember1.getId());
System.out.println(findMember1.getName());
System.out.println("#### END ####");

  • #### START ####와 #### END #### 사이에 쿼리 조회가 있다.
  • 조회된 객체의 클래스는 Proxy 클래스이다.

 

프록시 객체의 초기화

Member member = entityManager.getReference(Member.class, 1L);

member.getName();

 

프록시 유틸리티

프록시 인스턴스의 초기화 여부 확인

  • PersistenceUnitUtil.isLoaded(entity)
Member findMember1 = entityManager.getReference(Member.class, member1.getId());

System.out.println(entityManagerFactory.getPersistenceUnitUtil().isLoaded(findMember1));
//false

findMember1.getName();

System.out.println(entityManagerFactory.getPersistenceUnitUtil().isLoaded(findMember1));
//true

 

프록시 클래스 확인 방법

  • entity.getClass()
    • 실제 엔티티 객체인지, 프록시 객체인지 클래스 name을 알 수 있다.

 

프록시 강제 초기화

  • Hibernate.initialize(entity)
Member findMember1 = entityManager.getReference(Member.class, 1L);

Hibernate.initialize(findMember1);

 

'JPA' 카테고리의 다른 글

[JPA] 16. 영속성 전이(CASCADE)  (0) 2021.01.18
[JPA] 15. 즉시 로딩과 지연 로딩  (0) 2021.01.18
[JPA] 13. @MappedSuperclass  (0) 2021.01.11
[JPA] 12. 상속 관계 매핑  (0) 2021.01.11
[JPA] 11. 연관관계 매핑 - 다대다(N : M)  (0) 2021.01.11