1HOON
논리적 코딩
1HOON
전체 방문자
오늘
어제
  • HOME (186)
    • ☕️ JAVA (28)
      • WhiteShip Java LIVE Study (6)
      • Effective JAVA (10)
    • 🔮 KOTLIN (4)
    • 🌱 SPRING (51)
      • 스프링 인 액션 (22)
      • JPA (18)
    • ☕️ JAVASCRIPT (6)
    • 📃 DATABASE (40)
      • ORACLE (37)
      • MSSQL (2)
    • 🐧 LINUX (4)
    • 🐳 DOCKER (5)
    • 🐙 KUBERNETES (4)
    • 🏗️ ARCHITECTURE (8)
    • 📦 ETC (26)
      • TOY PROJECT (5)
      • RECRUIT (1)
      • 그냥 쓰고 싶어서요 (14)
    • 🤿 DEEP DIVE (1)
    • 🚽 Deprecated (9)
      • PYTHON (3)
      • AWS (2)
      • HTTP 완벽가이드 (3)
      • WEB (1)

블로그 메뉴

  • 홈
  • 방명록
  • 관리

인기 글

최근 글

티스토리

hELLO · Designed By 정상우.
1HOON

논리적 코딩

[JPA] 프록시
🌱 SPRING/JPA

[JPA] 프록시

2020. 7. 29. 15:51

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Member
{
    @Id @GeneratedValue
    private Long id;
 
    private Team team;
}
 
 
@Entity
public class Team
{
    @Id
    private Long id;
}
cs

 

이런 연관관계의 엔티티가 존재할 때, 아래 상황을 가정해보겠습니다.

 

Case 1. Member와 Team을 함께 출력하는 비즈니스 로직이 추가되었다.

이 때는 아래와 같이 로직을 구현할 수 있겠습니다.

JPA에서 Member 조회 시, 자동으로 Team까지 함께 조회해오므로, EntityManager.find 호출 시에 Member와 Team을 조인해서 가져오는 SELECT 쿼리를 수행하게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
/**
 * Member를 Team과 함께 출력한다.
 */
public void printMemberWithTeam()
{
    Member findMember = EntityManager.find(Member.class, 1L);
    Team team = findMember.getTeam();
 
    System.out.println(findMember.toString());
    System.out.println(team.toString());
}
Colored by Color Scripter
cs

 

그렇다면, 아래 경우에는 어떨까요?

Case2. Member만 출력하는 비즈니스 로직이 추가되었다.

Case2의
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

경우에는 Member 엔티티만을 활용하지만, JPA에서 연관관계 매핑되어있는 Team도 같이 조회하므로 불필요한 데이터도 함께 조회됩니다. 예시의 경우 단순히 Member-Team으로 구성되어 괜찮아 보일 수도 있지만, 엔티티 간 연관관계가 늘어날수록 문제는 커집니다.

 

JPA에서는 이러한 문제를 프록시와 지연 로딩(Lazy Loading)으로 해결할 수 있는데, 이번 포스팅에서는 프록시에 대해 알아봅니다.

 

 

프록시


1
EntityManager.getReference(Member.class, 1L);
cs

프록시는 기존의 find 메서드가 아닌 getReference 메서드를 통해 사용할 수 있습니다.

 

getReference 메서드를 호출하면 JPA에서는 데이터베이스에 SQL을 질의하는 대신, 가짜 엔티티 객체를 반환해줍니다. 그리고 이 가짜 엔티티 객체를 참조할 때, SQL을 질의하게 됩니다.

 

  • EntityManager.find(Member.class, 1L)
    Member를 조회하면서 Team도 같이 조회합니다.
  • EntityManager.getReference(Member.class, 1L)
    메서드 호출 시점에는 SQL이 질의되지 않고, 반환된 객체를 사용(참조)할 때 질의됩니다.
    이때, 반환된 객체는 실제 엔티티 클래스(Member)가 아니고 Hibernate에서 만든 프록시(가짜) 클래스입니다.

 

앞선 사례 중 Case2로 예를 들면, 아래와 같이 동작하게 됩니다.

1
2
3
4
5
6
7
8
public void printOnlyMember()
{
    // SQL 질의 X
    Member findMember = EntityManager.getReference(Member.class, 1L);
    
    // SQL 질의 O
    System.out.println(findMember.toString());
}
Colored by Color Scripter
cs

 

그런데, 코드를 자세히 보시면 이상한 부분이 있을 겁니다.

바로 getReference 메서드에서 반환하는 객체는 엔티티가 아니고 프록시 객체일 텐데 Member 변수로 담고 있는 부분입니다.

그 이유는 아래 프록시의 특징을 보시면 이해되실 겁니다.

 

 

프록시의 특징


  • 프록시는 실제 클래스를 상속받아 만들어집니다.
  • 프록시는 실제 클래스와 겉모양이 같습니다.
  • 따라서, 사용할 때는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됩니다.

프록시 객체 참조 시 SQL 질의 프로세스

  • 프록시 객체는 실제 객체의 참조(target)를 보관합니다.
  • 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메서드를 호출합니다.
    -> 프록시.getName() 을 호출하면 프록시에서 target.getName() 을 호출합니다.
  • 프록시 객체는 처음 사용할 때 한 번만 초기화됩니다.
  • 프록시 객체를 초기화한다고 해서 프록시 객체가 실제 엔티티 객체로 바뀌는 것은 아닙니다.
    초기화가 되면, 프록시 객체를 통해 실제 엔티티에 접근 가능합니다.
  • 프록시 객체는 원본 엔티티를 상속받기 때문에, 타입 체크 시 주의해야 합니다.
    == 비교는 실패합니다. 대신 instance of를 사용해야 합니다.
    ▶ 로직에서 프록시를 쓸지 안 쓸지 확신할 수 없기 때문에 타입 비교 시에는 반드시 instace of를 사용하는 것을 추천합니다.
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 getReference 메서드를 호출했더라도 실제 엔티티를 반환합니다.
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 예외가 발생합니다.
    예) Hibernate의 경우 org.hibernate.LazyInitializationException 발생

출처 :: 인프런 강의(자바 ORM 표준 JPA 프로그래밍 - 기본편)

반응형
저작자표시 비영리 변경금지 (새창열림)

'🌱 SPRING > JPA' 카테고리의 다른 글

[Spring Data JPA] Auditing에 ZonedDateTime 사용하기  (0) 2021.05.27
[JPA] 즉시 로딩/지연 로딩  (0) 2020.07.29
[JPA] 상속관계 매핑  (0) 2020.07.19
[JPA] 다양한 연관관계 매핑  (0) 2020.07.19
[JPA] 양방향 매핑시 주의점과 연관관계 편의 메서드  (0) 2020.07.18
    '🌱 SPRING/JPA' 카테고리의 다른 글
    • [Spring Data JPA] Auditing에 ZonedDateTime 사용하기
    • [JPA] 즉시 로딩/지연 로딩
    • [JPA] 상속관계 매핑
    • [JPA] 다양한 연관관계 매핑
    1HOON
    1HOON

    티스토리툴바