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();

 


이것도 읽어보세요