전체코드 github 주소(branch: blog/proxy)
JPA에서 Proxy(프록시)란 무엇일까? 한 번 살펴보자!!!
getReference
EntityManager에서 제공하는 메서드 중 `getReference`라는 메서드가 있다.
`getReference` 메서드는 Proxy를 통해서 데이터를 조회해온다.
이 메서드를 통해서 Proxy란 무엇인지, 어떻게 동작하는지 살펴보도록 하겠다.
코드를 통해 getReference의 동작을 살펴보자!
JpaMain.java
package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
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 {
// programming club 생성
Club club = new Club();
club.setName("programming");
club.setDescription("즐거운 프로그래밍?");
club.setCreator("code-mania");
em.persist(club);
// code-mania student 생성
Student student = new Student();
student.setName("code-mania");
student.setClub(club);
student.setCreator("code-mania");
em.persist(student);
em.flush();
em.clear();
System.out.println("0. ======================="); // 0번 checkpoint
Student refMember = em.getReference(Student.class, student.getId());
System.out.println("1. refMember.getClass() = " + refMember.getClass()); // 1번 print
System.out.println("2. refMember.getId() = " + refMember.getId()); // 2번 print
System.out.println("3. refMember.getName() = " + refMember.getName()); // 3번 print
System.out.println("=======================");
em.flush();
em.clear();
System.out.println("4. ======================="); // 4번 checkpoint
Student findMember = em.find(Student.class, student.getId());
System.out.println("5. findMember.getClass() = " + findMember.getClass()); // 5번 print
System.out.println("6. findMember.getId() = " + findMember.getId()); // 6번 print
System.out.println("7. findMember.getName() = " + findMember.getName()); // 7번 print
System.out.println("=======================");
tx.commit();
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
} finally {
em.close();
}
em.close();
emf.close();
}
}
getReference 메서드는 find메서드와 사용법이 동일하다.
(첫번째 인자로 조회할 테이블의 엔티티 클래스를, 두번째 인자로 조회하고싶은 데이터의 아이디를 넘겨준다.)
위 사진은 JpaMain.java의 실행결과이다.
왼쪽은 getReference() 메서드를 통해 조회한 refMember의 출력물이고(0번 ~ 3번),
오른쪽은 find() 메서드를 통해 조회한 findMember의 출력물이다(4번 ~ 7번).
그러면 getReference 메서드와 find 메서드를 비교해보면서 Proxy를 배워보자!!
1. class
위 사진의 1번과 5번에서 getClass()를 출력하고 있는 것을 볼 수 있다.
그런데 getReference()를 통해 조회한 Student는 Proxy 클래스가 반환되었고,
find()를 통해 조회한 Student는 직접 만든 Student Entity 클래스가 반환되었다.
즉, getReference 메서드를 통해 조회한 객체는 직접 만드는 Entity의 객체가 아니라
Hibernate가 만들어준 Proxy의 객체라는 것을 알 수 있다.
2. 쿼리가 DB로 전송되는 타이밍
find 메서드는 자신을 호출하는 시점에 DB에 쿼리를 날려서 데이터를 받아온다.
하지만 getReference 메서드는 조회한 객체를 실제로 사용하는 시점에 DB에 쿼리를 날려 데이터를 받아온다.
그렇다면 실제로 사용하는 시점이란 것은 대체 언제일까?
getReference의 로그를 살펴보면 getId를 출력할 때까지는 쿼리가 날아가지 않았다.
이유는 다음과 같다.
getReference 메서드를 호출할 때 우리는 두 번째 인자로 Id를 넘겨줬다.
그렇기때문에 Hibernate는 Id를 이미 알고 있었고, getId 메서드를 호출했을 때 쿼리가 날아가지 않았다.
하지만 그 후 getName 메서드를 호출했을 때에는 쿼리가 먼저 날아가고,
getName 메서드가 정상적으로 실행된 것을 확인할 수 있다.
즉, 실제로 사용하는 시점이란 Proxy 객체에서 자신이 가지지 않은 정보를 조회할 때가 된다.
앞서 class에서 살펴본 내용까지 종합해보면,
getReference 메서드는 데이터베이스 조회를 실제로 사용하는 시점까지 미루는
가짜(Proxy) 엔티티 객체를 반환하는 것이라고 정리된다.
Proxy 객체
이제 getReference 메서드가 프록시 객체를 반환하는 것도,
Proxy 객체가 DB 조회를 실제 사용 시점까지 미루는 것도 알았다.
그런데 이게 내부적으로는 어떻게 동작되는 걸까?
Proxy 클래스의 구조와 초기화 메커니즘을 알아보자!
Proxy Class
Proxy 클래스는 하이버네이트(JPA 구현체)가 알아서 만들어주는데,
실제 클래스를 상속받아서 만들어지기때문에 엔티티와 겉모양(메서드, 멤버변수[field])이 같다.
Proxy 내부에는 target(초기값: null)이 존재하는데 실제 데이터가 들어있는 Entity 객체를 참조하는 변수이고,
Proxy 객체의 메서드를 호출하면 Proxy 객체는 실제 객체(target)의 메서드를 호출해준다.
- 즉, Proxy는 Entity의 역할을 위임받는다고 할 수 있다.
Proxy가 Entity의 역할을 위임받기때문에 사용하는 객체가
Entity 객체인지 Proxy 객체인지 구분하지 않아도 된다.(이론상의 이야기) - 또한 Proxy 객체는 초기화되기 전까지는 틀만 있을 뿐 내부는 비어있는 것과 마찬가지이다.
Proxy 초기화
위 그림은 Proxy의 초기화 과정이다.
getName()이 호출되면 영속성 컨텍스트를 거쳐 Entity가 생성되고,
생성된 Entity는 Proxy의 target에 연결된다.
그 후 Proxy에서 호출되는 메서드들은 target의 메서드를 호출시키게 된다.
Proxy 객체를 초기화하는 과정은 1번만 이루어지며,
Proxy 객체를 초기화한다는 것은 Proxy 객체를 통해서 실제 엔티티에 접근 가능해지는 것을 의미한다.
이렇게 Proxy란 무엇이고 어떻게 동작하는지 살펴보았다.
다음 글에서는 Proxy를 좀 더 편하게 사용하게 해주는 library와 주의사항에 대해서 살펴보겠다!!
'JPA > Proxy' 카테고리의 다른 글
즉시로딩과 지연로딩 (0) | 2021.08.23 |
---|---|
Proxy 주의사항 (0) | 2021.08.23 |