본문 바로가기
자바/JPA

[JPA/Java] 프록시(Proxy)

by drCode 2023. 2. 27.
728x90
반응형

프록시 기초

 

프록시란?

 

프록시는 진짜 객체 대신에 가짜 객체로서 진짜를 대신해주는 개념

Hibernate는 지연 로딩을 구현하기 위해 프록시를 사용한다.

 

프록시를 사용할 때는, EntityManager에서 getReference() 를 사용하여 

 

진짜 객체를 참조하여 가짜 객체를 만든다.

 

진짜 객체를 호출할 때는 em.find() 를 사용하지만,

 

가짜 객체는 em.getReference() 를 사용한다.

 

가짜 객체를 만드는 프록시

프록시의 특징은 다음과 같다.

 

1) 실제 클래스를 상속 받아서 만들어진다.

2) 실제 클래스와 비슷하다.

3) 사용하는 입장에서는 진짜 객체인지, 프록시 객체인지 구분하지 않고 사용해도 된다.

프록시 객체는 진짜 객체를 상속받아서 만들어진다.

 

Member 클래스를 예제로, 

 

임의로 데이터를 생성하여 인스턴스를 생성하고, 

 

인스턴스를 영속시킨 다음,

 

객체를 참조하는 인스턴스가 원본 클래스의 객체를 실제로 상속받은 게 맞는지 확인하는 방법이 있다.

 

JpaMain.java

try {
    Member member1 = new Member();
    member1.setUserName("member1");
    em.persist(member1);

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

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

    // A instanceof B: A 객체가 B를 상속받았는지 확인하는 메서드, boolean type 으로 true와 false를 return 한다.
    System.out.println("refMember instanceOf member : " + (refMember instanceof Member));
}

 

instanceof 를 사용하면 실제로 프록시가 실제 객체를 만드는 클래스를 상속했는지 확인할 수 있다.

실제로 instatnceof 를 사용하면 true가 나와야 한다.

 

refMember = class hellojpa.Member$HibernateProxy$vvrhs0x9

 

refMember는 프록시 객체라서 Hibernate의 Proxy 객체로 표시된다.

 

정상적인 em.find() 로 얻어진 member 와 프록시 객체인 refMember "==" 동등 비교시

 

false가 나와야 한다.

 

프록시의 추가적인 특징으로는

 

프록시 객체는 실제 객체의 참조(target)를 보관하는데,

 

프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.

 

Proxy는 실제 Entity를 바라보고 있다.
프록시 객체 초기화 과정

(1) em.getReference()로 프록시 객체를 생성하여 getName()을 쓰면 초기화 과정이 시작된다.

(2) MemberProxy 객체에 Member target 값이 존재하지 않아서 JPA가 영속성 컨텍스트에 값을 받아오는 초기화 요청을 한다.

(3) 영속성 컨텍스트가 DB에서 조회하여 값을 받아온다.

(4) 실제 영속성 컨텍스트가 실제 Entity를 생성해준다.

(5) 실제 객체는 이제 값을 제대로 가지고 있기 때문에 Member target의 getName()을 호출해서 영속성 컨텍스트가 호출한 값을 가져올 수 있다.

(6) 프록시 객체에 target이 할당되면, 프록시 객체의 초기화 동작은 없어도 된다.

 

 

프록시 객체는 처음 사용할 때 한번만 초기화하면 된다.

 

프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아니다.

초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능하다.

 

프록시 객체는 원본 엔티티를 상속받는데, 이에 따라 타입 체크시 주의해야한다.

== 는 비교할 수 없다. 왜냐하면 하이버네이트가 만든 프록시 객체라서  false가 나온다.

대신 상속받았는지 확인할 수 있는 instanceof 를 사용해야한다.

 

영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티를 반환한다.

 

영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제가 발생한다.

(하이버네이트는 org.hibernate.LazyInitializationException 예외를 발생시킨다)

 

프록시 확인 방법

  - 프록시 인스턴스의 초기화 여부 확인

emf.getPersistenceUnitUtil().isLoaded(refMember)

 - 프록시 클래스 확인 방법

member.getUserName();

 - 프록시 강제 초기화

org.hibernate.Hibernate.initialize(entity);

※ 참고 : JPA 표준은 강제 초기화가 없다.

강제로 호출하려면 Entity의 getter를 하나 호출하면 된다.

ex) member.getName();

728x90
반응형

댓글