[JPA] 프록시와 연관관계 관리

2025. 7. 1. 15:42·DB Access/JPA

 

프록시와 연관관계 관리

#JPA/기본


/프록시:

  • 엔티티 조회시 연관된 엔티티까지 조회해야 할까?

/프록시 기초

  • em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회
  • em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회
  • /프록시 특징
    • 실제 클래스와 겉모양이 같다. 사용자는 구분하지 않고 사용하면 됨.
    • 프록시 객체는 원본 엔티티를 상속받음
      • 타입 체크 시== 대신 instanceof 사용
    • 프록시 객체는 실제 객체의 참조(target)를 보관하고, 실제 객체의 메서드를 호출한다(위임)
    • 프록시 객체는 처음 사용할 때 한 번만 초기화.
      • 프록시를 통해 실제 객체의 필드를 처음 조회시 실제 엔티티를 조회한다. (즉, 필드를 처음 조회시 쿼리 나감)
    • 영속성 컨텍스트에 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환
      • JPA는 같은 영속성 컨텍스트, 같은 트랜잭션 레벨 안에서 조회하면 항상 결과가 같아야 한다.
    • 반대로 프록시로 한번 조회가 되면, em.find에서 프록시를 반환한다.
      • 중요한 건, em.find를 해도 프록시 객체를 반환받을 수 있다는 거다.
    • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생
      • 트랜잭션이 끝나면 영속성 컨텍스트도 끝난다. 이 때 프록시를 조회하지 않도록 주의!
  • /프록시 확인
    • 프록시 인스턴스의 초기화 여부 확인: PersistenceUnitUtil.isLoaded(Object entity)
    • 프록시 클래스 확인: entity.getClass()...
    • 강제 초기화: org.hibernate.Hibernate.initialize(entity)
      • 하이버네이트가 제공하는 기능, JPA 표준에 강제 초기화 기능은 없음. 직접 getter() 호출해야 함.

/즉시 로딩과 지연 로딩

  • /지연 로딩 LAZY를 사용해서 프록시로 조회

 

Team team = new Team();
team.setName("teamA");
em.persist(team);

Member member1 = new Member();
member1.setUsername("member1");
member1.setTeam(team);
em.persist(member1);

Member member = em.find(Member.class, member1.getId());
System.out.println("member.getTeam().getClass() = " + member.getTeam().getClass());
// 프록시 객체가 나온다.

  • /즉시 로딩 EAGER를 사용해서 함께 조회
    • 즉시 로딩을 할 때 JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회한다.
  • /프록시와 즉시로딩 주의
    • 가급적 지연 로딩만 사용(특히 실무에서)
    • 즉시 로딩 쓰면 FK가 많을 수록 조인이 정말 많이 발생한다.
    • 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
      • em.find()를 사용하면 쿼리가 한 번 나간다. (최적화 O)
      • em.createQuery()를 사용하면 쿼리가 1 + N번 나간다.
        • List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
        • members 목록을 가져왔는데 Team 필드에 즉시로딩이 지정되어있음.
        • member가 10개고, 각각 다른 team을 가지고 있다면, 10개의 team 조회 쿼리가 나간다.
    • /N+1 문제 해결 방법
      • 지연 로딩 + fetch join(동적으로 조인)
        • List<Member> members = em.createQuery("select m from Member m join fetch m.team", Member.class).getResultList();
      • 엔티티 그래프
      • 배치 사이즈
    • @ManyToOne, @OneToOne은 기본이 즉시 로딩 LAZY로 설정
    • @OneToMany, @ManyToMany는 기본이 지연 로딩

/지연 로딩 활용 - 이론
/지연 로딩 활용 - 실무: 모든 연관관계에 지연 로딩을 사용해라! + JPQL fetch 조인이나, 엔티티 그래프 기능을 사용해라!


/영속성 전이: CASCADE

  • 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속상태로 만들도 싶을 때
    • ex) 부모 엔티티 저장할 때 자식 엔티티도 함께 저장
  • /영속성 전이: 저장
    • @OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
    • em.persist(parent) 하면 child까지 persist된다.
  • /영속성 전이: CASCADE - 주의!
    • 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없음. 단순히 편의 기능임.
  • /CASCADE 종류: 아래 경우에만 CASCADE를 하겠다.
    • ALL(CRUD…), PERSIST, REMOVE, MERGE, REFRESH, DETACH
  • /언제 CASCADE를 사용해야 하나?(실무에서)
    • 자식들을 하나의 부모가 관리할 때는 의미가 있다. 즉, 소유자가 하나일 때 / 즉, 라이프 사이클이 같을 때.
    • ex) 게시글 - 첨부파일
  • /고아 객체
    • 고아 객체 제거: 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
      • orphanRemoval = true
    • /고아 객체 - 주의
      • cascade처럼 참조하는 곳이 하나일 때 사용해야함!
      • 특정 엔티티가 개인 소유할 때 사용
      • @OneToOne, @OneToMany만 가능
      • 고아 객체 제거 기능을 활성화 하면, 부모를 제거할 때 자식도 함께 제거된다. CascadeType.REMOVE와 비슷
  • /영속성 전이 + 고아 객체, 생명주기
    • CascadeType.ALL + orphanRemoval=true
    • 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거
    • 두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있음
      • Aggregate Root를 구현할 때 유용하다.

Ref) 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편

'DB Access > JPA' 카테고리의 다른 글

[JPA] JPQL(객체지향 쿼리 언어) 정리  (0) 2025.07.01
[JPA] 값 타입  (0) 2025.07.01
[JPA] 고급 매핑 - 상속 관계 매핑  (0) 2025.07.01
[JPA] 다양한 연관관계 매핑  (0) 2025.07.01
[JPA] 연관관계 매핑 기초  (0) 2025.07.01
'DB Access/JPA' 카테고리의 다른 글
  • [JPA] JPQL(객체지향 쿼리 언어) 정리
  • [JPA] 값 타입
  • [JPA] 고급 매핑 - 상속 관계 매핑
  • [JPA] 다양한 연관관계 매핑
lumana
lumana
배움을 나누는 공간 https://github.com/bebeis
  • lumana
    Brute force Study
    lumana
  • 전체
    오늘
    어제
    • 분류 전체보기 (462)
      • 개발 일지 (0)
        • Performance (0)
        • TroubleShooting (0)
        • Refactoring (0)
        • Code Style, Convetion (0)
        • Architecture (0)
      • Software Engineering (36)
        • Test (8)
        • 이론 (18)
        • Clean Code (10)
      • Java (72)
        • Basic (5)
        • Core (21)
        • Collection (7)
        • 멀티스레드&동시성 (13)
        • IO, Network (8)
        • Reflection, Annotation (3)
        • Modern Java(8~) (13)
        • JVM (2)
      • Spring (53)
        • Framework (12)
        • MVC (23)
        • Transaction (3)
        • AOP (11)
        • Boot (0)
        • AI (0)
      • DB Access (16)
        • Jdbc (1)
        • JdbcTemplate (0)
        • JPA (14)
        • Spring Data JPA (0)
        • QueryDSL (0)
      • Computer Science (130)
        • Data Structure (27)
        • OS (14)
        • Database (10)
        • Network (21)
        • 컴퓨터구조 (6)
        • 시스템 프로그래밍 (23)
        • Algorithm (29)
      • HTTP (8)
      • Infra (1)
        • Docker (1)
      • 프로그래밍언어론 (15)
      • Programming Language(Sub) (76)
        • Kotlin (0)
        • Python (25)
        • C++ (51)
        • JavaScript (0)
      • FE (11)
        • HTML (1)
        • CSS (9)
        • React (0)
        • Application (1)
      • Unix_Linux (0)
        • Common (0)
      • PS (13)
        • BOJ (7)
        • Tip (3)
        • 프로그래머스 (0)
        • CodeForce (0)
      • Book Review (4)
      • Math (3)
        • Linear Algebra (3)
      • AI (7)
        • DL (0)
        • ML (0)
        • DA (0)
        • Concepts (7)
      • 프리코스 (4)
      • Project Review (6)
      • LegacyPosts (11)
      • 모니터 (0)
      • Diary (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
lumana
[JPA] 프록시와 연관관계 관리
상단으로

티스토리툴바