Free Lines Arrow
본문 바로가기
DataBase/JPA

[JPA] 프록시

by skahn1215 2021. 8. 11.
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

댓글