em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회 -> 바로 쿼리문을 날린다.
em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회 -> 실제로 사용될때 쿼리문을 날린다.
프록시의 구조
실제 클래스를 상속 받아서 만들어진다.
실제 클래스와 겉 모양이 같다.
사용하는 입장에서 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.
프록시 객체의 초기화
client 가 getName()을 호출하면 MemberProxy 는 영속성 컨텍스트에 Member 객체를 요청한다.
영속성 컨텍스트는 DB를 조회해서 실제 엔티티 객체를 생성한다.
그리고 Member target에 생성된 엔티티 객체를 연결한다.
이렇게 되면 target에 엔티티가 초기화 되므로 이후에 다시 조회할땐 바로 가져다 쓸수있다.
프록시의 특징
프록시 객체는 처음 사용할 때 한번만 초기화한다.
프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것이 아니다. -> 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근이 가능할 뿐이다.
프록시 객체는 원본 엔티티를 상속받는다. 따라서 타입 체크 시 주의해야한다. -> == 비교 실패, 대신 instance of 를 사용해야한다.
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference() 를 호출해도 실제 엔티티를 반환한다.
Member member1 = new Member();
member1.setUsername("member1");
em.persist(member1);
em.flush();
em.clear();
Member refMember = em.getReference(Member.class, member1.getId());
em.detach(refMember) // 여기서 준영속 상태가 되었다. em.close, em.clear 해도 마찬가지다
refMemver.getUsername() // 이 순간 에러 발생
위의 상황처럼 준영속 상태 일때 프록시를 초기화 하면 org.hibernate.LazyInitializationException 가 발생하고 실제로 실무에서 가장 많이 만나는 문제일 것이다.
프록시 확인
프록시 인스턴스의 초기화 여부 확인 : emf.getPersistenceUnitUtil.isLoaded(Object entity)
프록시 클래스 확인 방법 : entity.getClass()
프록시 강제 초기화 : org.hibernate.Hibernate.initialize(entity)