이 글은 김영한님의 JPA 강의 중 8장 프록시와 연관관계 관리를 듣고 정리한 내용입니다 :)
강의 : 자바 ORM 표준 JPA 프로그래밍 - 기본편
교재 : 자바 ORM 표준 JPA 프로그래밍🤷♀️
엔티티 조회는 2가지 방식으로 할 수 있는데,
- em.find() : 실제 엔티티 객체 조회하기
- em.getReference() : DB 조회를 미루는, 가짜 엔티티 객체를 조회 (DB에 쿼리가 안나감)
⇒ 실제 사용될 시점에 DB조회가 이뤄짐
이때, 2번 getReference 방법을 사용하면 가짜 엔티티 객체를 조회하게 되는데,
이때 이 가짜 엔티티를 프록시라 한다! 이 프록시에 대해서 알아보자-!
❔ 프록시란
- 프록시는 하이버네이트가 만든 가짜 클래스로 실제 객체의 참조를 보관한다.
- target 속성이
처음에는 null로 설정되어있다가, (처음에는 DB 조회 안 하니까)
실제 사용할 때 실제 DB 조회가 일어나고, 이때 실제 엔티티를 참조할 수 있게 바뀐다.
프록시 객체의 초기화 과정은 아래와 같다!

프록시의 특징
- 프록시는 처음 사용할 때 한 번만 초기화된다.
- 프록시 초기화할때, target 속성으로 실제 엔티티를 참조하는 것
- 프록시 객체가 실제 엔티티 객체로 바뀌는 것이 아니라, 원본 엔티티를 참조받는 것이다.
여기서 주의! 타입 비교할때, ==으로 비교하면 안 되고 instance of를 사용한다.
참고로 영속성 컨텍스트에 있으면 == 으로 했을 때 같은 값, 즉 동일성을 보장함 ⇒ JPA의 특징
- 준영속 상태일 때 프록시 초기화하면 문제 발생
⇒ org.hibernate.LazyInitializationException
em.detached(); 혹은 em.closed(); 으로 준영속 상태를 만들수 있음
em.clear()하면 영속성 컨텍스트를 꺼버림
둘다 프록시 객체를 초기화 못하고 에러 발생!
⇒ 아예 어노테이션으로 준영속상태 안되게끔 ⇒ 추가
프록시를 직접 확인하려면?
- 초기화했는지 확인하기 : PersistenceUnitUtil.isLoaded(Object entity)
Member refMember = em.getReference(Member.class, member1.getId()); //Proxy
// 프록시가 실제 사용된적이 없기에 초기화 안되었음
em.getPersistenceUnitUtil().isLoaded(); // false
- 프록시의 클래스 확인하기 : refMember.getClass()
Member refMember = em.getReference(Member.class, member1.getId()); //Proxy
// 프록시가 실제 사용된적이 없기에 초기화 안되었음
refMember.getClass();
- 프록시 강제 초기화시키기 : org.hibernate.Hibernate.initialize(entity명);
Member refMember = em.getReference(Member.class, member1.getId()); //Proxy
Hibernate.initialize(refMember); //강제 초기화
참고로 이 강제초기화 방법은 하이버네이트의 방식이고, JPA 표준은 강제 초기화 방법이 따로 없다.
하이버네이트 말고는, 강제 호출은 member.getName()을 사용할 것.
실무에서는 지연 로딩을 쓰자!
지연 로딩 : 프록시 객체로 실제 엔티티 객체 참조
즉시 로딩 : 연관관계에 있는 객체까지 바로 조회
// 즉시 로딩 - 패치 타입 EAGER 설정
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "team_id")
private Team team;
// 지연 로딩 - 패치 타입 LAZY 설정
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
즉시 로딩 대신에 지연로딩을 쓰는 이유?
- 즉시 로딩을 쓰면, 모든 테이블 다 연관시키고, 생각 못한 쿼리가 나간다.
- JPQL에서는 문제 발생한다. JPQL는 SQL로 해석하는 데, N+1 (추가쿼리)가 나간다.
=> 이론적으로는 자주 함께 사용하는 연관관계끼리는 즉시 로딩, 아니면 지연로딩을 쓰지만,
실무에서는 그냥 다 지연 로딩을 사용하자!
특히 @ManyToOne, @OneToOne은 기본이 즉시 로딩이니까 → LAZY로 바꾸어야 함!
'Back-end > JPA' 카테고리의 다른 글
| [JPA/김영한] 값 타입 (0) | 2022.06.23 |
|---|---|
| [JPA/김영한] 영속성 전이, 고아객체 (0) | 2022.06.23 |
| [JPA/김영한] 고급 매핑 (0) | 2022.06.23 |
| [JPA/김영한] 여러가지 연관관계 매핑 (0) | 2022.06.23 |
| [JPA/김영한] 연관관계 매핑하기 (0) | 2022.06.23 |