JPA Proxy 란 ?
JPA에서 프록시를 사용하는 이유와 특징, 그리고 활용 방법에 대해 자세히 알아보겠습니다.
* 왜 JPA 에서 프록시를 써야 할까 ?
만약 하나의 Entity 에서 다른 Entity 를 연관관계로 맺고 있다면, 단순한 SELECT 로직에서도 연관관계를 맺고 있는 Entity 까지 불러오게 된다. 이러한 과정이 반복되다 보면, 호출되는 SQL 들이 최적화되지 않는다.
Ex) Member 과 Team 이 연관관계를 맺고 있는데, Member 에 대한 정보만 사용한다면 Team 에 대한 정보는 호출되지 않아도 된다.
이러한 경우를 위해, JPA 에서는 Lazy-Loading, Proxy 를 제공한다.
// 데이터베이스를 통해 실제 Entity 객체 조회
em.find();
Member member = new Member();
member.setUserName("heejae");
em.persist(member);
em.flush();
em.clear();
// Member Entity 와 연관관계를 맺는 Team 에 대한 정보를 불러온다.
member = em.find(Member.class, member.getId());
/*
Hibernate:
select
member0_.MEMBER_ID as MEMBER_I1_1_0_,
member0_.TEAM_ID as TEAM_ID3_1_0_,
member0_.USERNAME as USERNAME2_1_0_,
team1_.TEAM_ID as TEAM_ID1_2_1_,
team1_.name as name2_2_1_
from
Member member0_
left outer join
Team team1_
on member0_.TEAM_ID=team1_.TEAM_ID
where
member0_.MEMBER_ID=?
*/
// 데이터베이스 조회를 미루는 가짜(Proxy) Entity 객체 조회
em.getReference();
Member member = new Member();
member.setUserName("heejae");
em.persist(member);
em.flush();
em.clear();
// Member Entity 와 연관관계를 맺는 Team 에 대한 정보를 불러온다.
member = em.getReference(Member.class, member.getId());
/*
em.getReference() 를 호출할 시점에는 SQL 을 호출하지 않는다. DB 를 호출하지 않고, Reference 만 반환해 준다. 하지만 이 값을 사용할 때에 SQL 을 사용한다.
*/
* Proxy 의 특징
-실제 클래스 Entity 를 상속받아서 만들어짐 (Hibernate가 자동으로 생성한다.)
-실제 클래스와 겉 모양이 같다.
-프록시 객체를 호출하면, 실제 객체의 메소드를 호출한다. (이 때에 영속성 컨텍스트를 통해 초기화가 이루어진다.)
-프록시 객체는 처음 사용할 때 한 번 초기화된다.
-프록시 객체를 초기화 할 때, 프록시 객체가 실제 Entity 로 바뀌는 것이 아니다. 단, 프록시 객체가 실제 Entity 를 상속받기 때문에, 사용해도 실제로 체감할 수 없다. 따라서 Type Check 시 주의해야 한다. (== 대신 instanceof 를 사용해야 한다.)
-영속성 컨텍스트에 찾는 엔티티가 이미 있으면, Reference 를 반환하지 않고 실제 값을 반환한다. (이 때, 반환 값은 Reference 객체가 아니라 실제 Entity Type 이 반환된다.)
-영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때 초기화하면, LazyInitializationException 이 발생한다.
[em.detach(object) OR em.close() OR em.clear() 등이 먼저 호출되면, Exception 이 발생된다.]

* 프록시 확인
// 프록시 인스턴스의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(Object entity);
// 프록시 클래스 확인
entity.getClass().getName()
// 프록시 강제 초기화
org.hibernate.Hibernate.initialize(entity);
// 참고
// JPA 준에서 강제 초기화 방법은 없다.
// 강제 호출 : member.getName();
이것도 읽어보세요