728x90
반응형
프록시
- JPA에서 객체를 조회 할때 find 함수 왜에도 getReference 가 있다.
- getReference 를 할 경우 프록시를 가져온다.
- 프록시는 일단 가짜 엔티티 라고 생각하자.
- 멤버가 팀을 가지고 있는 구조에서
- 멤버만 find 했을때 항상 Team 도 같이 불러 온다.
- 꼭 그래야 할까?
아니다 Proxy를 사용했을때 멤버에서 팀을 호출 하는 순간 쿼리가 조회된다.
프록시의 구조
- 실제 클래스를 상속 받아 만들어 진다.
- 실제 클래스와 겉 모양이 같다.
- 하이버네이트가 내부의 라이브러리를 사용해서 가짜 엔티티 객체를 준다.
- 아래 그림을 보면 실제 Entity 를 상속 받아서 Proxy가 만들어 진다.
- 프록시는 껍대기 라고 생각하면 된다.
- 실제로는 ID 값만 가지고 있다.
프록시는 어떤 역할을 하는가?
- 프록시는 실제 객체의 target(참조를) 가지고 있는다.
- 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다
프록시 객체 초기화
- 프록시 객체도 초기화를 해줘야 한다.
- 그렇지 않으면 Exception이 발생한다.
초기화 과정
1. Member member = new Member();
2. member.setName("Hi");
3. em.persist(member);
4. em.flush();
5. em.clear();
6. Member member = em.getReference(Member.class, "id1");
7. member.getName();
1~5 번까지는 JPA 영속성 컨텍스트에 저장후 DB에 저장하고 영속성 컨텍스트를 클리어 해주는과정이다.
6. 프록시를 가져온다. 여기서는 쿼리가 나가지 않는다.
7. getName()을 했을 경우 DB 에 접근하여 조회후 가져온다.
그림 설명
- 1. getName 을 호출한다.
- 2. target에 값이 없다면 초기화 요청을 한다.
- 3. 영속성 컨텍스트에 요청한다.
- 4. 실제 엔티티객체를 생성해준다.
- 5. target 과 객체가 연결된다.
특징
- 프록시 객체는 한번만 초기화 된다.
- 프록시 객체를 초기화 할때 객체가 실제 엔티티로 바뀌지 않는다.
- 참조 개념으로 생각하면 될것 같다.
- C++ 에서 포인터 개념? - 프록시 객체는 원본 엔티티를 상속받는다
- 타입 체크를 할때 비교가 아닌 instacne of 를 사용해야 된다. - 영속성컨텍스트에 찾는 엔티티가 이미 있다면 em.getReference를 호출해도 실제 엔티티가 반환된다.
- 준영속성일때 프록시 초기화시 문제 발생한다.
프록시 함수들
- PersistenceUnitUtil.isLoaded(entity):
- 프록시가 초기화 됐는지 확인 가능하다
- entity.getClass().getName():
- 프록시 클래스 확인 할 수 있다.
- org.hibernate.Hibernate.initialize(entity):
- 프록시를 강제 초기화 할 수 있다.
프록시 테스트 해보기
예제1: 프록시 호출
1. Member proxyMember = em.getReference(Member.class, member1.getId());
2. System.out.println("proxyMember: "+ proxyMember.getClass());
결과
proxyMember: class jpabook.jpashop.domain.Member$HibernateProxy$6T8rDw0h
1. 프록시 호출
예제2: 영속성에 이미 엔티티가 존재 할 경우
1. Member entityMember = em.find(Member.class, member1.getId());
2. Member proxyMember = em.getReference(Member.class, member1.getId());
3. System.out.println("entityMember: "+ entityMember.getClass());
4. System.out.println("proxyMember: "+ proxyMember.getClass());
결과
entityMember: class jpabook.jpashop.domain.Member
proxyMember: class jpabook.jpashop.domain.Member
1. 엔티티 영속성에 실제 객체가 들어간다.
2. 프록시로 호출해도 영속성 객체에 엔티티가 있기 때문에 엔티티를 가져온다.
3. 출력
예제3 비교해보기
비교1 프록시를 먼저 가져왔을 경우
1. Member proxyMember = em.getReference(Member.class, member1.getId());
2. Member entityMember = em.find(Member.class, member1.getId());
3. System.out.println("entityMember: "+ entityMember.getClass());
4. System.out.println("proxyMember: "+ proxyMember.getClass());
5. System.out.println("entityMember == proxyMember: "+ (proxyMember == proxyMember));
결과
entityMember: class jpabook.jpashop.domain.Member$HibernateProxy$a7bFWWmM
proxyMember: class jpabook.jpashop.domain.Member$HibernateProxy$a7bFWWmM
entityMember == proxyMember: true
비교2 객체를 먼저 가져 왔을 경우
1. Member entityMember = em.find(Member.class, member1.getId());
2. Member proxyMember = em.getReference(Member.class, member1.getId());
3. System.out.println("entityMember: "+ entityMember.getClass());
4. System.out.println("proxyMember: "+ proxyMember.getClass());
5. System.out.println("entityMember == proxyMember: "+ (proxyMember == proxyMember));
결과
entityMember: class jpabook.jpashop.domain.Member
proxyMember: class jpabook.jpashop.domain.Member
entityMember == proxyMember: true
비교 1과 2 가 둘다 트루다 왜냐면 JPA 는 같은 트랜잭션안에서 동일성을 유지 해야 되기 때문이다.
예제4 프록시 클래스 비교
find 로 두객체를 가져 왔을때
1. Member proxyMember = em.find(Member.class, member1.getId());
2. Member entityMember = em.find(Member.class, member2.getId());
3. System.out.println("entityMember: "+ entityMember.getClass());
4. System.out.println("proxyMember: "+ proxyMember.getClass());
5. System.out.println("entityMember == proxyMember: "+ (entityMember.getClass() == proxyMember.getClass()));
결과
entityMember: class jpabook.jpashop.domain.Member
proxyMember: class jpabook.jpashop.domain.Member
entityMember == proxyMember: true
1 ~ 2: 객체를 가져왔다.
5. 동일한 객체 이니까 true이다 즉 타입이 같다
하나는 프록시 다른 하나는 find 로 엔티티를 가져온경우
1. Member proxyMember = em.getReference(Member.class, member1.getId());
2. Member entityMember = em.find(Member.class, member2.getId());
3. System.out.println("entityMember: "+ entityMember.getClass());
4. System.out.println("proxyMember: "+ proxyMember.getClass());
5. System.out.println("entityMember == proxyMember: "+ (entityMember.getClass() == proxyMember.getClass()));
결과
entityMember: class jpabook.jpashop.domain.Member
proxyMember: class jpabook.jpashop.domain.Member$HibernateProxy$q2lFS77G
entityMember == proxyMember: false
5. 비교를 했을때 false 가 뜨는 경우는 타입이 다르다 하나는 프록시 객체 다른 하나는 엔티티객체 타입이기 때문이다.
타입 비교를 할때는 instance of 를 사용해야 된다.
728x90
반응형
'DataBase > JPA' 카테고리의 다른 글
[JPA] CASCADE (0) | 2021.08.22 |
---|---|
[JPA] Lazy 와 Eager (0) | 2021.08.19 |
[JPA] @MappedSuperclass (0) | 2021.08.09 |
[JPA] 고급 매핑1: 상속관계 맵핑 (0) | 2021.08.08 |
[JPA] 다양한 연관관계 맵핑 (0) | 2021.08.07 |
댓글