BE전문가 프로젝트

Proxy(프록시) 본문

JPA

Proxy(프록시)

원호보고서 2022. 10. 30. 22:43

프록시 기초

JPA에는 데이터를 가져올 때 em.find() 뿐만 아니라 em.getReference()라는 메소드가 존재한다.

- em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회

- em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시)엔티티 객체 조회(데이터를 조회시 select 쿼리가 나가지 않음)

 

public class JPAMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();                                     //트랜잭션 시작

        try {

            Member member = new Member();
            member.setCreatedBy("kim");

            em.persist(member);

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

            Member foundMember = em.getReference(Member.class, member.getId());

            System.out.println("foundMember's Id = " + foundMember.getId());

            tx.commit();
        }catch (Exception e){
            tx.rollback();
        }finally {
            em.close();
        }
        emf.close();
    }
}

위에 코드를 실행해보면 foundMember에서 select쿼리가 나가는 것이 아닌 foundMember.getId()를 실행할 때, 즉 실제로 사용될 때 select쿼리가 나간다.

 

프록시 특징

  • 실제 클래스를 상속 받아서 만들어짐
  • 실제 클래스와 겉 모양이 같다.
  • 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨(이론상)
  • 프록시가 내부적으로 라이브러리를 이용하여 만든다

  • 프록시 객체는 실제 객체의 참조(target)를 보관
  • 프록시 객체를 호출하면 프록시ㅣ 객체는 실제 객체의 메소드 호출

프록시 객체의 초기화

Member member = em.getReference(Member.class, "id1");
member.getName();

1. getName() 호출을 할 때 처음에는 Member target에 값이 없다는 것을 인지한다

2. 영속성 컨텍스트에 초기화를 요청을 한다.(진짜 값 요청)

3. DB를 조회한다.

4. 실제 Entity를 생성한다.

5. Proxy 내부에 있는 target과 entity를 연결시켜준다.

 

프록시 특징

  • 프록시 객체는 처음 사용할 때 한 번만 초기화
  • 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능
  • 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함(== 비교실패, 대신 instance of 사용)
  • 연속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환                                  - 이미 member는 1차 캐시에 있기 때문에 Proxy로 가져올 필요가 없기 때문                                                                  - JPA에서는 ==비교가 한 영속성 컨텍스트에서 가져온 객체이며, PK가 같으면 항상 true를 보장해야 한다.  따라서        proxy로 객체를 다시 가져오게 되면 == 비교시 false가 나오기 때문에 실제 엔티티를 반환하는 것이다.
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화 문제 발생                                                  (하이버네이트는 org.hibernate.LazyInitialization 예외를 터트린다.)

 

프록시 확인

  • 프록시 인스턴스의 초기화 여부 확인                                                                                                                                - emf.PersistenceUnitUtil.isLoaded(Object entity)
  • 프록시 클래스 확인 방법                                                                                                                                                    - entity.getClass().getName() 출력(..javasist.. or HibernateProxy...)
  • 프록시 강제 초기화                                                                                                                                                            - org.hibernate.Hibernate.initialize(entity);
  • 참고: JPA표준은 강제 초기화 없음                                                                                                                                   - 강제 호출: member.getName()

프록시를 공부한 이유

getReference는 잘 쓰이지 않는다. 그럼에도 불구하고 공부한 이유는 proxy의 메커니즘을 이해해야 즉시 로딩과 지연 로딩에 대해서 이해가 가능하기 때문이다

'JPA' 카테고리의 다른 글

영속성 전이 :CASCADE  (0) 2022.10.31
즉시 로딩과 지연 로딩  (0) 2022.10.31
쇼핑몰 만들기 3 - 상속관계 매핑  (0) 2022.10.30
@MappedSuperclass  (0) 2022.10.30
상속관계 매핑  (0) 2022.10.29
Comments