자바 ORM 표준 JPA 프로그래밍(인프런)

Ch08. 프록시와 연관관계 관리 - 프록시

webmaster 2021. 12. 15. 12:54
728x90

Member를 조회할 때 Team도 함께 조회해야 할까?

  • 회원과 팀을 함께 출력할 때도 있지만 회원만 출력할 때도 있다

프록시 기초

프록시객체를 리턴

  • em.find() vs em.getReference()
  • em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회
  • em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회

프록시 특징

  • 실제 클래스를 상속받아서 만들어짐
  • 실제 클래스와 겉모양이 같다.
  • 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨(이론상)
  • 프록시 객체는 실제 객체의 참조(target)를 보관
  • 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메서드 호출

프록시 객체의 초기화

프록시 동작 과정

Member member = em.getReference(Member.class, “id1”);
member.getName();​

프록시 객체의 특징

  • 프록시 객체는 처음 사용할 때 한 번만 초기화
  • 프록시 객체를 초기화할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능
    • 프록시는 유지가 되고, target에만 값이 채워지는 것이다.
  • 프록시 객체는 원본 엔티티를 상속 받음, 따라서 타입 체크 시 주의해야 함 (== 비교 실패, 대신 instance of 사용)
private static void logic(Member m1,Member m2){
    //System.out.println("m1 == m2 " + (m1.getClass() == m2.getClass()));

    //실제 로직은 매개변수로 프록시객체가 넘어오는지 실제 객체가 넘어오는지 알수 없다.
    //절대 ==을 사용하지 말고, instanceof를 사용하자
    System.out.println("m1 == m2 " + (m1 instanceof Member));
}
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해 도 실제 엔티티 반환
System.out.println("m1 = "+m1.getClass());

Member reference = em.getReference(Member.class,member1.getId());
System.out.println("reference = "+reference.getClass());//Member 클래스가 나온다, 프록시 객체 x
//1. 이미 영속성 컨텍스트에 있다면 프록시로 얻는 이점이 없기 때문에 Member객체를 반환
//2. JPA는 같은 트랜젝션안에서의 id가 같다면 항상 같은 객체임을 보장해 주어야 하므로 == 이 성립되어야 하는데, 프록시 객체를 반환하면 ==이 성립이 안되기 떄문에 Member를 반환
System.out.println("a == a: " + (m1 == reference));
Member refMember = em.getReference(Member.class,member1.getId());
System.out.println("refMember = "+refMember.getClass());

Member findMember = em.find(Member.class,member1.getId());
System.out.println("findMember = "+findMember.getClass());
//findMember 가 프록시가 반환이 된다.
//em.find를 해도 프록시가 반환이 될 수도 있다는 뜻이다.
// == 을 맞추기 위해 결국 프록시로 맞춰져서 true가 나온다.
System.out.println("refMember == findMember: " + (refMember == findMember));
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생 (하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)
Member member1 = new Member();
member1.setUsername("hello1");
em.persist(member1);

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

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

//em.detach(refMember);//더이상 영속성컨택스트가 관리 안해줄때
em.clear();

refMember.getUsername(); //프록시를 부를때
//에러 발생

프록시 확인

프록시 확인

  • 프록시 인스턴스의 초기화 여부 확인 : PersistenceUnitUtil.isLoaded(Object entity) 
  • 프록시 클래스 확인 방법 : entity.getClass().getName() 출력(..javasist.. or HibernateProxy…)
  • 프록시 강제 초기화 : org.hibernate.Hibernate.initialize(entity); 
    • 참고: JPA 표준은 강제 초기화 없음 강제 호출: member.getName();
728x90