프로젝트 git url | github.com/rtef23/JpaStudy |
필요 | 1. docker 2. docker-compose |
프로젝트 정보 | mysql : 5.7 - docker image java : 1.8 |
Hibernate 프록시
사용 이유
- 조회시 연관관계에 있는 데이터를 사용하는 경우에만 추가로 조회하도록 하는 방법(지연로딩, 즉시로딩)
특징
- 실제 클래스를 상속 받아서 생성됨
- 동일성 비교 결과 값은 false이다.
- 아래 예제에서는 영속성 컨텍스트에서 객체를 가져오지 않는 경우를 가정
- 타입 비교시에는 반드시 instanceof 연산자로 비교해야한다.
- 동일성 비교 결과 값은 false이다.
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 |