[JPA] 프록시 (JPA 기본편 by 김영한)

4779 단어 JPA_기본JPAJPA

프록시

em.find()를 하면 DB나 영속성 컨텍스트 안에서 해당 엔티티 객체를 찾아 반환합니다. 이와 다르게 em.getReference()를 하면 데이터 베이스 조회를 미루는 프록시 엔티티 객체를 반환합니다. (이때 영속성 컨텍스트에 캐시 안에 해당 객체가 있다면 프록시가 아닌 진짜 객체를 반환하니 주의!)



프록시 객체

프록시 객체는 실제 클래스를 상속 받아서 만들어진 겉모양만 똑같은 가짜 객체입니다. 그리고 target이라고 해서 실제 엔티티 객체의 참조를 저장할 수 있습니다. 하지만 처음에는 target에 null이 들어가 있어서 아무것도 연결되지 않은 상태입니다.

프록시 객체가 동작하는 것을 코드를 보면서 살펴보겠습니다. member에는 프록시 객체가 들어갑니다. 그 뒤에 (1)getName을 호출하면 프록시객체는 그런 함수가 없기 때문에 영속성 컨텍스트에 (2)초기화를 요청합니다. 그러면 (3)영속성 컨텍스트는 DB에서 실제 그 Member객체를 찾아서 (4)실제 엔티티 객체와 프록시 객체를 연결해줍니다. 그러면 이제 프록시 객체는 (5)member.getName()을 target.getName()으로 처리할 수 있게 됩니다.



주의해야할 점

프록시 객체와 실제 엔티티 객체를 이어주는 초기화 과정은 한번만 일어나고 그 뒤로는 target에 들어온 엔티티에 접근해서 함수들을 호출해 나갑니다. 따라서 초기화 후에 프록시 객체가 실제 엔티티 객체로 대체되는 것이 아닌 프록시 객체가 실제 엔티티 객체로 접근할 수 있게 된 것입니다. 마지막으로 초기화 과정은 영속성 컨텍스트를 매개해서 이뤄지기 때문에 영속성 프록시 객체가 준영속 상태라면 초기화를 요청해도 오류가 납니다.




예제

// 프록시객체 : 실제 클래스와 겉모양이 같음
Member findMember = em.getReference(Member.class, member.getId()); // 쿼리 안나감 

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

// 쓰일 때 쿼리날림 (초기화)
System.out.println("findMember.getUsername = " + findMember.getUsername());
           
// 초기화 되서 초기화 요청 안함
System.out.println("findMember.getUsername = " + findMember.getUsername());

여기서 findMember.getId()에서 초기화가 되지 않는 이유는 em.getReference때에 member.getId()로 id값을 넘겼기 때문에 프록시객체가 id값은 저장을 했기 때문입니다.

따라서 getUsername시에 쿼리가 날라가며 최초로 초기화가 되고 그뒤에 getUsername을 한번더 호출할 때는 이미 target에 실제 객체가 연결되었기 때문에 쿼리가 날라가지 않습니다.

마지막으로 findMember.getUsername()처럼 초기화를 시킬 수 도있지만 아래와 같이 강제 초기화를 할 수도 있습니다.

org.hibernate.Hibernate.initialize(entity);

좋은 웹페이지 즐겨찾기