본문 바로가기
Develop/Java

[JPA] 프록시와 영속성 전이

by 연로그 2022. 1. 13.
반응형

이전에 읽으면 좋은 글

 

목차

  1. 프록시
  2. 영속성 전이

 


1. Proxy; 프록시

 

  • Entity Manager의 .getReference()를 통해 DB 조회를 미루는 가짜(프록시) 엔티티 객체를 조회
  • 실제 클래스를 상속 받아 생성해 겉 모양이 같음
  • 사용자는 진짜 객체인지 프록시 객체인지 구분 X
  • 실제 객체의 참조(target)를 보관
  • 프록시 객체에서 메소드 호출 시, 프록시 객체가 실제 객체의 메소드를 호출
    (아래 동작 과정 참고)

 

proxy 동작 과정

Member member = em.getReference(Member.class, “id1”);
member.getName();
  1. getReference()를 통해 프록시 객체 생성
  2. 사용자가 프록시 객체의 getName() 호출
  3. 프록시 객체가 영속성 컨텍스트에 초기화 요청
  4. 영속성 컨텍스트가 DB를 조회해 실제 Entity 생성
  5. 프록시의 Member target에서 getName() 호출
  6. getName() 결과 반환

 

proxy의 초기화

  • 프록시는 첫 사용 시 단 한번만 초기화
  • 초기화 != 실제 Entity 객체로 대체
    프록시 객체를 통해 실제 Entity 객체로 접근 가능하게 되는 것이지 주소나 값이 대체되는 것이 아님

 

proxy와 타입체크

JPA는 한 트랜잭션에서 같은 객체를 가져온 경우, ==의 결과가 항상 true로 나와야 한다.
  • 프록시 객체는 원본 Entity를 상속받아 사용
    • = Entity와 프록시 객체를 '=='으로 비교 시 false
    • 👉 instanceof 사용해야 함
  • 영속성 컨텍스트에 찾는 Entity가 이미 존재하는 경우, getReference()를 해도 실제 Entity를 반환
  • getReference() 후에 find()로 찾은 객체는 Entity가 아닌 Proxy

 

proxy와 준영속 상태

Member 안에 Team이라는 객체가 존재한다고 가정한다.

Member에 대한 정보만 필요할 때 Team의 정보까지 전부 조회하는건 불필요한 일이다.

이를 방지하기 위해 지연 로딩(LAZY키워드)를 이용해 Team의 조회를 미뤄둘 수 있다.

(+ 즉시 로딩은 EAGER 키워드)

 

👉 Team프록시로 조회하기 때문!

 

@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    
    // getter, setter ... 
}

 

🔻 지연 로딩의 디폴트 설정

더보기

각 연관 관계별 지연 로딩/즉시 로딩 디폴트 설정은 아래와 같다.

 

  • @ManyToOne, @OneToOne의 디폴트: 즉시 로딩
  • @OneToMany, @ManyToMany의 디폴트: 지연 로딩

 

🔻 실무에서 지연 로딩 사용하기

더보기

실무에서는 가급적이면 지연 로딩만 사용하는 것이 좋다.
즉시 로딩을 적용하면 예상치 못한 SQL이 발생할 수 있고 JPQL의 경우 N+1 문제를 일으킬 수 있다.

N+1 문제: 쿼리문 실행 후 Member를 가져온다. Team이 즉시로딩으로 세팅되어 있으면 Team의 select 쿼리문을 하나 더 실행시킨다.

 

(+) 지연 로딩으로 설정 후 Team의 정보를 가져오려면 patch join이나 Entity Graph 기능을 사용.

 


2. 영속성 전이

 

  • CASCADE
  • 특정 Entity를 영속화 하는 경우 연관 Entity까지 함께 영속화
  • != 연관 관계
@OneToMany(mappedBy = "test", cascade = CascadeType.PERSIST)

 

CASCADE의 종류

  • ALL: 모두 적용
  • PERSIST: 영속
  • REMOVE: 삭제
  • MERGE: 병합
  • REFRESH: refresh
  • DETACH: detach

 

🔻 영속성 전이 예제 살펴보기

더보기

Parent Entity

@Entity
public class Parent {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<Child> children = new ArrayList<>();
    
    // getter, setter, ...
}

 

Child Entity

@Entity
public class Child {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;

    // getter, settter, ...
}

 

위와 같은 관계가 있다고 가정한다.

Parent는 Child를 CascadeType.ALL로 지정해두었다.

 

만약 Parent의 children 필드에 값을 insert하면, Child Entity와 관련된 테이블에도 insert 된다.

만약 children 필드에 값을 delete하면, Child Entity와 관련된 테이블에도 delete가 진행된다.

 

🔻 고아 객체와 영속성 전이

더보기

고아 객체

  • 부모 엔티티와 연관 관계가 끊어진 자식 엔티티
  • orphanRemoval = true로 하면 자동으로 고아 객체가 삭제 됨
  • @OneToMany, @OneToOne에서만 가능
  • CascadeType.REMOVE처럼 작동

 

고아 객체와 영속성 전이는 로직 작성 시 어떤 Entity까지 영향이 가느냐를 고려하게 되므로 신중히 사용해야 함.
보통 소유자가 단 하나일 때나 DDD의 Aggregate Root에서 사용.


본 게시글은 김영한 님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편' 강의를 구매 후 정리하기 위한 포스팅입니다.

내용을 임의로 추가, 수정, 삭제한 부분이 많으며 정확한 이해를 위해서 강의를 구매하시는 것을 추천 드립니다.

https://inf.run/yR3B

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 본 강의는 자바 백엔

www.inflearn.com

반응형

'Develop > Java' 카테고리의 다른 글

[Java] toString() 파헤치기  (2) 2022.02.15
[JPA] Table 'DB.hibernate_sequence' doesn't exist 에러  (0) 2022.01.14
[JPA] Entity 매핑  (0) 2022.01.10
[JPA] JPA란?  (0) 2022.01.03
[Java] Enum에 대해  (4) 2021.12.13