들어가며
8강은 1편과 2편으로 나눠 진행하였다.
8.1 프록시
- 엔티티가 실제 사용될 때까지 데이터베이스 조회를 지연하는 방법을 지연 로딩이라 한다.
- 그런데 지연 로딩 기능을 사용하려면 실제 엔티티 객체 대신에 데이터베이스 조회를 지연할 수 있는 가짜 객체가 필요한데 이것을 프록시 객체라 한다.
- JPA 표준 명세는 지연 로딩의 구현 방법을 JPA 구현체에 위임 -> 하이버네이트
- 하이버네이트는 지연 로딩을 지원하기 위해 프록시를 사용하는 방법과 바이트코드를 수정하는 두 가지 방법을 제공
8.1.1 프록시 기초
- JPA 에서 식별자로 엔티티 하나로 조회할 때 find() 사용 -> 영속성 컨텍스트에 엔티티가 없으면 디비 조회 -> 이렇게 직접 조회하면 조회한 엔티티를 실제 사용하든 하지 않든 데이터베이스를 조회하게 된다. -> 엔티티를 실제 사용하는 시점까지 미룰때는 getReference()를 사용
- getReference() 사용 시 디비를 조회하지 않고 실제 엔티티 객체도 생성하지 않는다 -> 대신 디비 접근을 위임한 프록시 객체를 반환
- 프록시의 특징 1
- 프록시 클래스는 실제 클래스를 상속 받아서 만들어지므로 실제 클래스와 겉 모양이 같음
- 프록시 객체는 실제 객체에 대한 참조를 보관
- 프록시 객체의 메서드를 호출하면 프록시 객체는 실제 객체의 메서드를 호출함
- 프록시 객체의 초기화
- 프록시 객체는 member.getName() 처럼 실제 사용될 때 데이터베이스를 조회해서 실제 엔티티 객체를 생성하는 것을 프록시 객체의 초기화라 함
-
- 프록시 객체에 member.getName() 호출해서 실제 데이터 조회
-
- 실제 엔티티가 생성되어 있지 않으면 영속성 컨텍스트에 실제 엔티티 생성을 요청함 (초기화)
-
- 영속성 컨텍스트는 데이터베이스를 조회해서 실제 엔티티 객체 생성
-
- 프록시 객체는 생성된 실제 엔티티 객체의 참조를 Member target 멤버변수에 보관
-
- 프록시 객체는 실제 엔티티 객체의 getName()을 호출해서 결과 반환
- 프록시의 특징 2
- 처음 사용할 때 한 번만 초기화 됨
- 초기화 한다고 실제 엔티티로 바뀌는 것이 아닌 프록시 객체를 통해서 실제 엔티티에 접근할 수 있는 것
- 프록시 객체는 원본 엔티티를 상속받은 객체이므로 타입 체크 시에 주의해서 사용 필요
- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 디비를 조회할 필요가 없으므로 em.getReference()를 호출해도 프록시가 아닌 실제 엔티티를 반환
- 초기화는 영속성 컨텍스트의 도움을 받아야 가능
- 따라서 영속성 컨텍스트에 도움을 받을 수 없는 준영속 상태의 프록시 객체를 초기화하면 문제 발생(org.hibernate.LazyInitializationException)
8.1.2 프록시와 식별자
- 엔티티를 프록시로 조회할 때 식별자(PK) 값을 파라미터로 전달하는데 프록시 객체는 이 식별자 값을 보관
- 프록시 객체는 식별자 값을 가지고 있으면 조회해도 프록시 초기화하지 않음 (단, 엔티티 접근 방식을 프로퍼티(@Access(AccessType.PROPERTY))로 설정한 경우에만 초기화하지 않음 -> 접근 방식 필드(@Access(AccessType.FIELD))로 했을때는 초기화 함)
- 연관관계를 설정할 때는 식별자 값만 사용하므로 프록시를 사용하면 디비 접근 횟수를 줄일 수 있음. —>>>>> 접근 횟수
- 참고로 연관관계 설정할 때는 엔티티 접근 방식을 필드로 설정해도 프록시를 초기화하지 않음 ->??
8.1.3 프록시 확인
- JPA가 제공하는 PersistenceUnitUtil.isLoaded(Object entity) 메서드 사용 시 프록시 인스턴스의 초기화 여부 확인 가능 (초기화되지 않은 프록시 인스턴스는 false 반환)
- 프록시 강제 초기화
- 하이버네이트의 initialize() 사용시 강제 초기화 가능
- JPA 표준에는 프록시 강제 초기화 메서드가 없음
8.2 즉시 로딩과 지연 로딩
- 즉시 로딩
- 엔티티를 조회할 때 연관된 엔티티도 함께 조회
- 설정 방법 : FetchType.EAGER - 1 : 1
- 지연 로딩
- 연관된 엔티티를 실제 사용할 때 조회
- 설정 방법 : FetchType.LAZY - 1 : N
8.2.1 즉시 로딩
- 회원과 팀 테이블을 즉시 로딩으로 조회하면 각각의 두 테이블을 조회하는 것이 아닌 JPA 구현체는 즉시 로딩을 최적화하기 위해 가능하면 조인 쿼리를 사용함
- NULL 제약 조건과 JPA 조인 전략 (참고)
- 즉시 로딩 실행 SQL에서 JPA가 내부 조인(INNER JOIN)이 아닌 외부 조인(LEFT OUTER JOIN)을 사용한 것을 유심히 봐야함
- 회원 테이블의 TEAM_ID 외래 키는 NULL 값을 허용하지 않음 -> 따라서 팀에 소속되지 않은 회원이 있을 가능성 있음 -> 팀에 소속하지 않은 회원과 팀을 내부 조인하면 팀은 물론이고 회원 데이터도 조회 불가
- 이러한 상황을 고려해 외부 조인을 사용하는 것
- 하지만 외부 조인보다 내부 조인이 성능과 최적화에서 더 유리
- 내부 조인 사용 시 외래 키에 NOT NULL 제약 조건을 설정하면 값이 있는 것을 보장하게 되고 JPA에 코드로 @JoinColumn에 nullable = false 설정 시 내부 조인을 사용하게 됨
8.2.2 지연 로딩
- 멤버를 조회하게 되면 팀은 조회하지 않고 조회한 회원의 team 멤버변수에 프록시 객체를 넣어둠
- 해당 프록시 객체는 실제 사용될 때까지 데이터 로딩을 미룸
- 참고로 조회 대상이 영속성 컨텍스트에 이미 있으면 프록시 객체를 사용할 이유가 없음
- 따라서 프록시가 아닌 실제 객체를 사용함
8.2.3 즉시 로딩, 지연 로딩 정리
- 지연 로딩 : 연관된 엔티티를 프록시로 조회한다. 프록시를 실제 사용할 때 초기화하면서 데이터베이스를 조회한다.
- 즉시 로딩 : 연관된 엔티티를 즉시 조회, 하이버네이트는 가능하면 SQL 조인을 사용해서 한 번에 조회
- 지연 로딩과 즉시 로딩 중 어느 것을 사용하면 좋은지는 상황에 따라 다름
'Book' 카테고리의 다른 글
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (9) 값 타입 (0) | 2022.09.18 |
---|---|
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (8-2) 프록시와 연관관계 관리 (0) | 2022.09.10 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (7-2) 고급매핑 (0) | 2022.08.24 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (7-1) 고급매핑 (0) | 2022.08.17 |
자바 ORM 표준 JPA 프로그래밍 - JPA 정리하기 (6) 다양한 연관관계 매핑 (0) | 2022.08.12 |
댓글