[ Intro ]
- 사전적 의미로
대리(행위)나 대리권, 대리인을 의미
--> 어떤 것을 대신 해주는 것
- 객체 내부에
특정 값
이 아닌,객체
를 참조하는객체지향 설계
에서프록시의 필요성
에 대해 알아보자객체
를 조회할 때 마다내부 참조 객체
에 대한 모든 정보를 가져올 필요는 없다
-->프록시 객체
를 가져와서 필요할 때 가져오는 방식을 구현
[ 프록시 기초 ]
em.find()
vsem.getReference()
em.find()
: DB를 통해 실제 Entity 객체 조회em.getReference()
: DB조회를 미루는 가짜(프록시) Entity 객체 조회
프록시 객체
:실제 Entity
를 가리키는target
이라는 값만 들어있다
(최초에target
은null
값 보유)
이후실제 객체
에 접근하는method(getter)
를 호출하면 그 때 실제 DB에 접근해서target
에실제 Entity
가 연결된다
프록시 객체
동작 원리와프록시 객체 초기화
- 동작 원리1)
em.getReference()
로 호출하여 프록시 객체가 반환
(target
에는null
이 들어가 있다)
2) 실제 객체에 접근하는member.getName()
호출
3)target
이null
이기 때문에영속성 컨텍스트
에 요청
4)영속성 컨텍스트
는 실제 DB에서 조회한 후target
에실제 Entity
를 연결
5)프록시 객체
의target
을 통해.getName()
호출됨프록시 객체 초기화
: target이 실제 Entity와 연결되는 과정을 의미하는 것
(프록시 객체 초기화
는 최초 1회만 수행된다)
- 추가 확인 메서드's
- 프록시 인스턴스 초기화 여부 확인
:EntitiyManagerFactor
를 통해 얻는다
emf.getPersistenceUnitUtil().isLoaded(Object object)
- 프록시 클래스 확인
:entity.getClass().getName()
출력- 프록시 강제 초기화
:org.hibernate.Hibernate.initialize(entity);
(강제 초기화 수행시 그때 select가 나가서 Entity 연결해줌!)
[ 프록시 특징 & 주의점 ]
프록시 객체 초기화
는 최초 사용시 처음에만 수행프록시 객체 초기화
시프록시 객체
가실제 Entity
로 바뀌는 것이 아니다
-->프록시 객체
의target
에실제 Entity
가 연결될 뿐!
프록시 객체
는원본 Entity
를 상속받기 때문에 타입체크(TypeCheck) 주의!
--> 항상 class Type Check는==
이 아니라instance of
사용!
영속성 컨텍스트
에찾는 Entity
가 이미 있으면,em.getReference()
해도실제 Entity
가 반환된다
-->JPA
는 동일 객체임을 보장하기 위해프록시
를 먼저 조회하면프록시 객체
로 맞추고, 아니면실제 Entity
에 맞추는내부로직
을 가지고 있음
(그래서JPA
는 하나의트랜잭션
에서같은 객체
의 조회는 항상동일 객체
를 보장한다!)
영속성 컨텍스트
의 도움을 받을 수 없는준영속 상태
에서프록시 초기화
하면 오류 발생!
--> 결국프록시
는영속성 컨텍스트
를 통해 일어난다는 것이 핵심.
(LazyInitializationException
)
[ 개념 ]
내부 참조 객체
를실제 사용
하는 시점에 조회하는 로딩 방법- 내부 참조 객체의 사용을 잘 하지 않는 로직에 효율적(이론적)
-->실제 실무
에서 모두LAZY
로 설계하고 필요시한방로딩
을 해주는것이 더 효율적
[ 사용 ]
연관관계 매핑 어노테이션
에fetch 옵션
적용 (FetchType.LAZY
)
[ 개념 ]
내부 참조 객체
를조회 시점
에 함께 조회하는 로딩 방법
[ 사용 ]
[ 주의할 점 ]
즉시 로딩
으로 발생하는 여러가지 문제점들이 있다
- 예상하지 못한 SQL 실행
: JOIN이 많이 발생되기 때문에 훨씬 더 많은 쿼리가 실행됨JPQL
에서N+1문제
발생
: 앞에서는em.~()
로 우리가PK
를 알 수 있는 상황이었다.
하지만,JPQL
은PK
를 알 수 없고, 단순히SQL
로 변환되어 실행된다
-->PK
를 모르니까 알기 위해select query
실행
--> (데이터가N
개인 테이블(Member)전체 데이터 조회
시)
전체 객체 조회 1번(Member)
+내부에 참조되어 있는 객체 조회 N번(Team)
--> 나는 그저select * from member
만 원하는데 실제Team
테이블PK를 포함한 값
을 얻기 위한 쿼리들이N번
이나 실행된다
- 필요에 따라 데이터를 한번에 가져와야 한다면 아래 방법 이용
JPQL fetch 조인
--> 보통 사용엔티티 그래프
기능
모든 연관관계
에서지연 로딩
을 사용하자- 실무에서
즉시 로딩
은 사용하지 말자
JPQL
에서N+1문제
발생- 예상치 못한 쿼리 발생
- 필요에 따라 한번에 가져와야 한다면 아래 방법 이용
JPQL fetch 조인
--> 보통 사용엔티티 그래프
기능@ManyToOne
/@OneToOne
은 기본이즉시 로딩
--> 항상지연 로딩
으로 직접 설정 해줘야 한다
(@OneToMany
/@ManyToMany
는 기본이지연 로딩
)