본문 바로가기
Java/JPA

[JPA_Basic] Proxy

by HJ0216 2023. 9. 8.

이 글은 김영한의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: Java

- DB: H2 Database

- IDE: IntelliJ

 

 

em.find();와 em.getReference();

em.find(): DB에서 실제 Enitty 객체 조회

em.getReference(): DB 조회를 미루는 Proxy Entity 객체 조회

 

 

Proxy 객체

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

- 프록시 객체는 실제 객체의 참조를 보관

- 프록시 객체 호출 시, 프록시 객체는 실제 객체의 메서드 호출

* Proxy 객체의 초기화: Proxy 객체의 Target Entity를 생성하는 과정

1. getName() 요청

2. Proxy 객체의 target이 연결되어있는지 확인

→ 없을 경우, 영속성 컨텍스트에 target에 연결할 Entity 객체 요청

3. 영속성 컨텍스트에서 DB 조회 후,

4. 실제 Entity 객체 생성하여 반환

5. 반환된 Entity 객체를 Proxy 객체의 target에 연결

 

 

Proxy 객체 특징

- Proxy 객체는 처음 사용할 때 한 번만 초기화

- Proxy 객체를 초기화하더라도 Entity 객체로 바뀌는 것이 아니라, Proxy 객체를 통해서 실제 Entity 객체에 접근하는 것

    * 타입 체크 시, == 비교가 아닌 instance of 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            Member member2 = new Member();
            member2.setName("user2");
            em.persist(member2);
 
            em.flush();
            em.clear();
 
            Member m1 = em.find(Member.class, member1.getId());
            Member m2 = em.getReference(Member.class, member2.getId());
 
            logic(m1, m2);
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
 
    private static void logic(Member m1, Member m2) {
        System.out.println("m1 == m2: " + (m1.getClass() == m2.getClass()));
    }
 
}
 
 

🚨 logic()을 구현하여, getClass == 비교 시 false가 반환 됨

    - m1.getClass(): Entity

    - m2.getClass(): Proxy

m1, m2가 어떤 객체로 넘어올 지 알 수 없으므로 instance of 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            Member member2 = new Member();
            member2.setName("user2");
            em.persist(member2);
 
            em.flush();
            em.clear();
 
            Member m1 = em.find(Member.class, member1.getId());
            Member m2 = em.getReference(Member.class, member2.getId());
 
            logic(m1, m2);
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
 
    private static void logic(Member m1, Member m2) {
        System.out.println("m1 Class: " + (m1 instanceof Member));
        System.out.println("m2 Class: " + (m2 instanceof Member));
    }
 
}
 
 

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            em.flush();
            em.clear();
 
            Member findMember = em.find(Member.class, member1.getId());
            System.out.println("findMember.id = " + findMember.getClass());
 
            Member refMember = em.getReference(Member.class, member1.getId());
            System.out.println("refMember.id = " + refMember.getClass());
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
}
 
 

refMember.getClass()가 Entity 객체로 반환되는 이유

    - 같은 영속성 컨텍스트 안에 같은 PK를 통해 가져온 객체는 == 비교 시, JPA는 항상 true를 반환해야 함

    - 영속성 컨텍스트에 존재하는 Entity 대신 Proxy를 가져올 이점이 없음

+ 만일 getReference()로 Proxy 객체를 먼저 조회할 경우는 findMember가 Proxy 객체가 반환됨

    - 같은 영속성 컨텍스트 안에 같은 PK를 통해 가져온 객체는 == 비교 시, JPA는 항상 true를 반환해야 함

 

- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 org.hibernate.LazyInitializationException 발생

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            em.flush();
            em.clear();
 
            Member refMember = em.getReference(Member.class, member1.getId());
            System.out.println("refMember.id = " + refMember.getClass());
 
//            em.detach(refMember);
//            em.close();
            em.clear();
 
            System.out.println("refMember.id = " + refMember.getName());
            // org.hibernate.LazyInitializationException
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
}
 
 

 

 

 Proxy 객체 확인

- Proxy 인스턴스 초기화 여부 확인

    - emf.getPersistenceUnitUtil().isLoaded(refMember);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Main {
    public static void main(String[] args) {
        
        // 생략
 
        try {
 
            Member member1 = new Member();
            member1.setName("user1");
            em.persist(member1);
 
            em.flush();
            em.clear();
 
            Member refMember = em.getReference(Member.class, member1.getId());
            System.out.println("refMember.id = " + refMember.getClass());
            System.out.println("isLoaded: " + emf.getPersistenceUnitUtil().isLoaded(refMember)); // false
 
            refMember.getName();
            System.out.println("isLoaded: " + emf.getPersistenceUnitUtil().isLoaded(refMember)); // true
 
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
    }
 
}
 
 

 

- Proxy 강제 초기화

    - Hibernate.initialize(refMember);

 

'Java > JPA' 카테고리의 다른 글

[JPA_Basic] Cascade  (0) 2023.09.11
[JPA_Basic] N+1 문제  (0) 2023.09.10
[JPA_Basic] @MappedSuperclass  (0) 2023.09.05
[JPA_Basic] 상속관계 매핑  (0) 2023.08.23
[JPA_Basic] ERD를 기반으로 한 Entity 작성  (0) 2023.08.17