프록시

tyghu77·2023년 11월 13일
0

프록시란?

em.find() : DB를 통해서 실제 엔티티 객체 조회
em.getReference() : DB 조회를 미루는 가짜(프록시) 엔티티 객체 조회 (쿼리가 안나가는데 객체가 조회됨) getReference()를 할때는 쿼리가 나가지 않지만 나중에 진짜 쓰이는 곳이 나올 때 쿼리가 나간다.

실제 엔티티를 상속받아서 만들어졌기 때문에 실제 클래스와 겉모양이 같지만, 프록시 객체는 실제 객체의 참조를 보관한다.

이론상 사용하는 입장에서는 진짜인지 프록시인지 구분하지않고 그냥 사용하면 된다.

프록시 객체는 실제 객체의 참조를 보관하기 때문에 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.

프록시 객체의 초기화

Member member = em.getReference(Member.class, "id1");
member.getName();
  1. 프록시 객체의 Member target이 null이다.
  2. JPA가 영속성 컨텍스트에 진짜 객체를 요청한다. (초기화 요청)
  3. 영속성 컨텍스트가 DB를 조회하여 실제 엔티티를 생성한 후 프록시 객체의 target과 진짜 객체를 연결해준다.
  4. 프록시 객체의 getName()을 했을 때, 타겟을 통해 진짜 객체의 getName()을 반환한다.

프록시의 특징

  1. 프록시 객체는 처음 사용할 때 한 번만 초기화 된다.
  2. 프록시 객체를 초기화 할 때 프록시 객체가 실제 엔티티로 바뀌는것이 아니다. 초기화 되면 프록시 객체를 통해 실제 엔티티에 접근 가능한 것이다.
  3. 프록시 객체는 원본 엔티티를 상속받기 때문에, 타입 체크 시에 ==이 아닌 instanceof를 사용해야 한다. 프록시로 넘어올지, 진짜 객체로 넘어올지 모르기 때문이다.
  4. 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출했을 때 실제(진짜) 엔티티가 반환된다. 그 반대도 마찬가지이다. 처음에 프록시로 조회한다면 em.find()를 해도 프록시를 반환한다.
Member member1 = new Member();
member1.setName("member1");
em.persist(member1);

em.flush();
em.clear();

Member refMember = em.getReference(Member.class, member1.getId());
System.out.println("refMember = " + refMember.getClass()); //Proxy

Member findMember = em.find(Member.class, member1.getId());
System.out.println("findMember = " + findMember.getClass());

System.out.println("(refMember == findMember) = " + (refMember == findMember));

위의 경우에는 ==을 보장해야하기 때문에 두개 다 프록시가 반환된다.

이유는 다음과 같다.

이미 영속성 컨텍스트에 있는 것이므로 프록시로 가져와 봐야 아무 이점이 없다.
JPA에서는 한 트랜잭션 안에서 동일성을 보장하는데, 그 때문에 트랜잭션 안에서 가져온 객체들의 == 연산을 항상 true로 보장해준다. 이 때문에 프록시를 2개 호출하여도 같은 프록시를 쓴다.

  1. 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제가 발생한다.
    org.hibernate.LazyInitializationException 예외가 터진다.

프록시 확인

  1. 프록시 인스턴스의 초기화 여부 확인
    PersistenceUnitUtil.isLoaded(Object entity)
    emf.getPersistenceUnitUtil.isLoaded(Object entity)

  2. 프록시 클래스 확인 방법
    entity.getClass()출력

  3. 프록시 강제 초기화
    org.hibernate.Hibernate.initialize(entity)
    Hibernate.initialize(entity);
    JPA 표준은 강제 초기화 없음

profile
배운것을 기록하자

0개의 댓글