Member를 조회할 때 Team도 함께 조회해야 할까?
- Member와 Team이 연관관계를 가짐
- Member 객체만 필요한 상황
- Team까지 메모리에 올라오면 리소스의 낭비
이때 지연로딩을 사용한다면?
- Team을 지연로딩으로 처리
- Team을 제외한 Member의 상태만으로 로직 처리
- 리소스의 낭비를 줄임
지연로딩을 이해하기 위해 먼저 프록시를 이해해보자
프록시 기초
• 뒤에 나올 지연로딩이라는 기술을 가능하게 하는 것이 프록시이다
• em.find() vs em.getReference()
• em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회
• em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회
프록시 특징(1)
• 실제 클래스를 상속 받아서 만들어짐
• 실제 클래스와 겉 모양이 같다.
• 이론상 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.
프록시 특징(2)
• 프록시 객체는 실제 객체의 참조(target)를 보관
• 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출
프록시 객체의 초기화
Member member = em.getReference(Member.class, “id1”);
member.getName();
프록시의 특징(3)
- 프록시 객체는 처음 사용할 때 한 번만 초기화
- 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초기화되면 프록시 객체를 통해서
실제 엔티티에 접근 가능 - 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함 (== 비교 실패, 대신 instance of 사용)
- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환
(JPA의 효율성, 객체의 동일성 보장) - 처음 em.getReference()로 프록시를 호출하면 이후에 em.find()를 호출해도 프록시가 반환 (객체의 동일성 보장)
- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화(영속성 컨텍스트 초기화)하면 문제 발생
(하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)
em.getReference()
Member findMember = em.getReference(Member.class, member.getId()); //프록시 조회
System.out.prinln("findMember.id = " + findMember.getId()); //쿼리X
System.out.prinln("findMember.username = " + findMember.getUsername()); //쿼리O
※ 객체의 동일성 보장이란?
영속성 컨텍스트는 영속 상태의 엔티티들에 대한 동일성(Identity)을 보장한다.
1차 캐시에 의해, member 객체 하나를 두 번 조회해도 이 둘은 같은 객체(레퍼런스)가 된다.
애플리케이션 차원에서 1차 캐시에게 repeatable read인 트랜잭션 격리 수준을 제공한다.
(트랜잭션 격리수준은 크게 네 가지가 존재한다. 그중 하나가 repeatable read이다.)
※ repeatable read란?
같은 트랜잭션에서 같은 질의를 사용했을 때 질의를 아무리 여러번 해도 그리고 다른 트랜잭션에서 아무리 여러 번 그 행을 변경해도 항상 같은 데이터만 읽어드리는 경우를 Repeatable read라고 합니다.
프록시 확인
프록시 인스턴스의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(Object entity)
프록시 클래스 확인 방법
entity.getClass().getName() // 출력(..javasist.. or HibernateProxy…)
프록시 강제 초기화
org.hibernate.Hibernate.initialize(entity);
참고: JPA 표준은 강제 초기화 없음 강제 호출
member.getName()
※ "instanceof" 와 "==" 차이
instanceof 연산자는 상속 관계에 있는 객체를 두고 자식 여부를 확인하는 것이고,
== 연산자는 서로 다른 객체의 주소값의 일치 여부를 확인할 때 사용됩니다.
※ get.class()란?
자바(Java)에서는 Object 클래스에 있는 getClass()를 통해 여러가지 클래스에 대한 메타 속성 정보를 얻을 수 있습니다.이 처럼 객체를 통해 클래스의 정보를 분석하는 것을 리플렉션(reflection - java.lang.reflect)이라 하고 주로 클래스를 동적으로 로딩하거나 디컴파일(컴파일된 소스를 다시 반대로 되돌리는 일련의 과정) 할 때 많이 활용됩니다.
TestClass testClass = new TestClass();
System.out.println("getName() : " + testClass.getClass().getName());
System.out.println("getSimpleName() : " + testClass.getClass().getSimpleName());
// getFields()
for(Field field : testClass.getClass().getFields()){
System.out.println("field: " + field.getName()); //testClass의 필드를 출력
}
// getMethods()
for(Method method : testClass.getClass().getMethods()){
System.out.println("method: " + method.getName()); //testClass의 메서드를 출력
}
getName() - 각 패키지명이 포함된 클래스명
getSimpleName() - 패키지 경로가 포함되지 않은 클래스명
getFields() - 클래스의 필드 목록을 뽑아냄
getMethods() - 클래스의 메서드 목록을 뽑아냄
'JPA' 카테고리의 다른 글
JPA - 영속성 전이(CASCADE)와 고아 객체 (2) | 2022.04.11 |
---|---|
JPA - 즉시 로딩과 지연 로딩 (0) | 2022.04.11 |
JPA - @MappedSuperclass (0) | 2022.04.10 |
JPA - 상속관계 매핑 (0) | 2022.04.10 |
JPA - 다양한 연관관계 매핑(4) : 다대다 [N : M] (0) | 2022.04.10 |
댓글