JPA

JPA Proxy

으엉어엉 2024. 8. 25. 16:36
728x90

Proxy 란?

클라이언트와 Web서버의 사이에 위치하고 있어, 대신 통신을 받아 주는 것이 Proxy 서버이다. 

 

 

private static void printMember(Member member) {
    System.out.println("member = "+member.getUsername());
}

private static void printMemberAndTeam(Member member) {
    String username = member.getUsername();
    Team team = member.getTeam();
    System.out.println("회원 이름: " + username);
    System.out.println("소속팀: " + team.getName());
}

상황에 따라 팀과 멤버 둘을 쓸 때도 있고, 멤버만 쓸 일이 있을때가 있을 수 있다.

지연 로딩이나 프록시로 이것을 해결하기도 한다.

 

 

target이 진짜 reference

 

//            Member findMember = em.find(Member.class, member.getId());
//            System.out.println(findMember.getId());
//            System.out.println(findMember.getUsername());
            Member findMember = em.getReference(Member.class, member.getId());
            System.out.println(findMember.getClass());
            System.out.println(findMember.getId());
            System.out.println(findMember.getUsername()); //DB에 있는것.

 

Username을  DB에 쿼리를 날려서 알아낸다. 

 

 

class hellojpa.Member$HibernateProxy$owrPBaep => 가짜를 만든다.

Hibernate: 
    select
        m1_0.MEMBER_ID,
        m1_0.INSERT_MEMBER,
        m1_0.createdDate,
        m1_0.UPDATE_MEMBER,
        m1_0.lastmodifiedDate,
        t1_0.TEAM_ID,
        t1_0.INSERT_MEMBER,
        t1_0.createdDate,
        t1_0.UPDATE_MEMBER,
        t1_0.lastmodifiedDate,
        t1_0.name,
        m1_0.USERNAME 
    from
        Member m1_0 
    left join
        Team t1_0 
            on t1_0.TEAM_ID=m1_0.TEAM_ID 
    where
        m1_0.MEMBER_ID=?

 

 

프록시 특징

• 실제 클래스를 상속 받아서 만들어짐

• 실제 클래스와 겉 모양이 같다.

• 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨(이론상)

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

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

 

 

 

getReference로 프록시 객체를 가져온다.

getName을 호출하면 Member target의 처음 값이 없다. 그러면 2번이 실행된다.

영속성 컨택스트를 통해 초기화를 요청한다. 

DB를 통해 진짜 값을 가져와서 Entity를 만들어내는 과정을 말한다.

 

영속성 컨텍스트란? 

영속성 컨텐스트란 엔티티를 영구 저장하는 환경이라는 뜻이다. 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

 

• 프록시 객체는 처음 사용할 때 한 번만 초기화

• 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능하다.

• 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함 (==  비교 실패, 대신 instance of 사용)

아래와 같은 예시를 들 수 있다.

Member member1 = new Member();
member1.setUsername("hello2");
em.persist(member1);

Member member2 = new Member();
member2.setUsername("hello2");
em.persist(member2);

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

Member m1 = em.find(Member.class, member1.getUsername());
Member m2 = em.find(Member.class, member2.getUsername());

System.out.println("m1 ==  m2" + (m1.getClass()==m2.getClass()));//True가 당연히 나오긴 함.

Member m1 = em.find(Member.class, member1.getUsername());
Member m2 = em.getReference(Member.class, member2.getUsername());

System.out.println("m1 ==  m2" + (m1.getClass()==m2.getClass()));//이러면 False가 나오게 된다. 
(m1 instanceof Member) // 이러한 비교를 해야한다.

 

 

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

Member m1 = em.find(Member.class, member1.getId());
System.out.println(m1.getClass());

Member Reference = em.getReference(Member.class, member1.getId());
System.out.println(Reference.getClass());

m1 = class hellojpa.Member
Reference = class hellojpa.Member

 

Reference 가 먼저 오거나 두개다 일 경우 같은 Proxy 값을 같게 된다. 같은값을 가지는 이유는 두개를 == 하였을 때 True가 나와야하기 때문이다.

 

1차 캐쉬에 올라가 있는데 굳이 Proxy를 가져올 필요가 없다

 

• 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생 (하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)

 

 

 

프록시 확인

• 프록시 인스턴스의 초기화 여부 확인 PersistenceUnitUtil.isLoaded(Object entity)

• 프록시 클래스 확인 방법 entity.getClass().getName() 출력(..javasist.. or HibernateProxy…)

• 프록시 강제 초기화 org.hibernate.Hibernate.initialize(entity);

 

 

728x90

'JPA' 카테고리의 다른 글

JPA 영속성 전이 CASCADE  (0) 2024.08.25
JPA 즉시 로딩과 지연 로딩  (0) 2024.08.25
JPA 상속관계 맵핑  (0) 2024.08.22
JPA 연관관계 매핑  (0) 2024.08.22
JPA 연관관계  (0) 2024.08.21