이 글은 김영한의 [자바 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 |