JPA

프록시(Proxy)

dev-rootable 2023. 8. 17. 12:42

📌 프록시란

 

엔티티를 상속 받은 프록시

 

실제 클래스를 상속받아 만들어진 가짜 또는 원본을 위임받은 가벼운 객체

 

프록시 객체는 getClass() 정보에 "HibernateProxy"라는 정보가 붙는다.

 

프록시 객체의 getClass()

 

📌 프록시 조회

 

  • em.find() : 영속성 컨텍스트로부터 타깃 엔티티를 조회, 만약 영속성 컨텍스트에 타깃이 없다면 DB에서 조회 후 영속성 컨텍스트에 저장 ➡ SQL 전송 o, 조회 o
  • em.getReference() : DB 조회를 미루는 가짜(프록시) 엔티티 객체 조회SQL 전송 x, 조회 o
    • getReference() 시점이 아닌 getter 시점에 DB에 조회 SQL을 날림

 

 

id는 이미 알고 있는 정보이므로 쿼리 x, getName 시점에 쿼리 나감

 

📌 특징

 

1. 프록시는 실제 객체의 참조(target)을 보관한다.

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

 

프록시는 엔티티의 참조를 보관

 

2. 실제 클래스를 상속받아 만들어지므로, 사용하는 입장에서는 구분하지 않고 사용 가능하다.

 

3. 프록시 객체는 처음 사용할 때 한 번만 초기화한다. 즉, 초기화한 값을 계속 사용하고 더 이상 값을 초기화하지 않는다.

 

4. 프록시 객체를 초기화할 때, 프록시 객체가 실제 엔티티로 교체되는 것이 아니다.

 - 단지, 초기화되면 프록시 객체의 target 값이 채워져 실제 엔티티에 접근이 가능해진다.

 

5. 프록시 객체는 원본 엔티티를 상속받으므로, 타입 체크 시 '==' 대신 instance of를 사용해야 한다.

 - 해당 엔티티가 프록시일지 실제일지 모르기 때문에 JPA는 웬만하면 instance of를 사용하는 것이 좋다.

 

m1(entity) == m2(entity) : true

 

m1(entity) == m2(proxy) : false

 

m1(entity) or m2(proxy) instanceof Entity : true

 

6. em.find를 통해 영속성 컨텍스트에 실제 엔티티가 이미 들어 있다면 em.getReference를 호출해도 실제 엔티티를 반환

 - 이미 원본을 가져온 상태에서 프록시를 내어 얻는 이점이 없다. (성능 최적화에도 원본이 유리)

 

7. JPA는 한 트랜잭션/영속성 컨텍스트 내에서 같은 엔티티를 조회했을 때 같음을 보장한다. (동일성 보장)

 - 엔티티든 프록시든 한 트랜잭션/영속성 컨텍스트에서는 조회할 때마다 같은 객체가 반환된다.

 

m1 ➡ entity, reference ➡ entity

 

reference도 실제 entity가 반환됨

 

m1 ➡ proxy, reference ➡ proxy

 

m1과 reference는 동일한 proxy를 받음

 

8. 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 예외가 발생함

 

준영속 상태 refMember를 초기화

 

LazyInitializationException 발생

 

📌 프록시 객체의 초기화

 

최초 target은 빈 값

 

  1. Client는 getName()을 호출
  2. 생성된 프록시 객체는 영속성 컨텍스트에 초기화 요청
  3. 영속성 컨텍스트는 실제 엔티티를 DB에 조회
  4. 영속성 컨텍스트는 조회된 실제 엔티티를 생성
  5. 프록시 객체는 갖고 있던 참조(target)를 통해 실제 엔티티와 연결하여 getName() 결과를 얻음

 

🔎 프록시 확인

 

EntityManagerFactory를 통해 프록시 인스턴스의 초기화 여부를 확인할 수 있다.

 

refMember(proxy) 조회

 

refMember는 영속성 컨텍스트에 없음(초기화 x)

 

refMember(proxy) 초기화

 

refMember는 영속성 컨텍스트에 있음(초기화 o)

 

em.getReference()로 생성되는 프록시 객체도 영속성 컨텍스트에서 관리한다.

 

📌 프록시를 사용하는 이유

 

프록시는 원본 객체를 위임받은 가벼운 객체이기 때문에 메모리나 성능상 이점을 갖고 있다.

 

먼저, 지연 로딩을 생각해 볼 수 있다. 지연 로딩은 단독 조회를 최적화하기 위해 불필요한 조인을 하지 않고 연관 엔티티를 프록시로 가져온다. 그래서 조인으로 인한
성능 저하를 막을 수 있고,
실제 엔티티 대신 프록시가 들어오게 되므로 메모리 상 이점도 있다.

 

지연 로딩이 아니더라도 연관된 엔티티를 사용하지 않는 화면이 있을 때, 프록시를 사용하는 것이 효과를 볼 수 있다. 사용하지 않는 연관 엔티티일 경우, 실제 엔티티가
아니라 빈 껍데기인 프록시를 갖고 있으면 위에 언급한 이점을 가질 수 있기 때문이다.

 

Reference:

https://www.inflearn.com/questions/517669/proxy-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%9D%B4%EC%A0%90-%EC%82%AC%EC%9A%A9%EC%9D%B4%EC%9C%A0

 

Proxy 객체의 이점? 사용이유? - 인프런 | 질문 & 답변

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼

www.inflearn.com

 

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

초급자를 위해 준비한 [웹 개발, 백엔드] 강의입니다. JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자

www.inflearn.com