
[ 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는 기본이지연 로딩)