군만두의 IT 개발 일지

[스터디10] 08. 프록시와 연관관계 관리 본문

학습일지/Java

[스터디10] 08. 프록시와 연관관계 관리

mandus 2025. 8. 17. 01:39

목차

    8장. 프록시와 연관관계 관리

    - 프록시와 즉시로딩, 지연로딩
    - 영속성 전이와 고아 객체

    8.1 프록시

    • 엔티티를 조회할 때 연관된 엔티티들이 항상 사용되는 것은 아니다. 예를 들어, 회원 엔티티를 조회활 때 회원과 연관된 팀 엔티티까지 데이터베이스에서 조회하는 것은 비효율적이다.
    • 따라서 JPA는 엔티티가 실제 사용될 때까지 데이터베이스 조회를 지연하는 지연 로딩 방법을 제공한다.
    • 지연 로딩 기능을 사용하려면 실제 엔티티 객체 대신에 데이터베이스 조회를 지연할 수 있는 가짜 객체인 프록시 객체가 필요하다.

    8.1.1 프록시 기초

    JPA에서 식별자로 엔티티 하나를 조회할 때는 EntityManager.find()를 사용한다.

    • EntityManager.find(): 영속성 컨텍스트에 엔티티가 없으면 데이터베이스를 조회한다.
    • EntityManager.getReference(): 엔티티를 실제 사용하는 시점까지 데이터베이스 조회를 미룬다. JPA는 데이터베이스를 조회하지 않고 실제 엔티티 객체도 생성하지 않는 대신에 프록시 객체를 반환한다.

    프록시의 특징

    • 프록시 클래스는 실제 클래스를 상속 받아서 만들어지므로 실제 클래스와 겉 모양이 같다. 그래서 == 비교 대신 instanceof를 사용해야 하는 경우도 있다.
    • 프록시 객체는 실제 객체에 대한 참조를 보관한다.
    • 프록시 객체의 메소드를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.
    • 프록시 객체는 처음 사용할 때 한 번만 초기화된다. 프록시 객체가 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근할 수 있다.
    • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 데이터베이스를 조회할 필요가 없으므로 em.getReference()를 호출하면 실제 엔티티를 반환한다.
    • 초기화는 영속성 컨텍스트의 도움을 받아야 가능하다. 준영속 상태의 프록시를 초기화하면 LazyInitializationException 문제가 발생한다.

    8.1.2 프록시와 식별자

    엔티티를 프록시로 조회할 때 식별자(PK) 값을 파라미터로 전달하는데 프록시 객체는 이 식별자 값을 보관한다.

    • 엔티티 접근 방식을 프로퍼티(@Access(AccessType.PROPERTY))로 설정한 경우, 식별자 값을 조회하는 메소드를 호출해도 프록시를 초기화하지 않는다.
    • 엔티티 접근 방식을 필드(@Access(AccessType.FIELD))로 설정하면 JPA는 해당 메소드가 어떤 일을 하는지 알지 못하므로 프록시 객체를 초기화한다.

    8.1.3 프록시 확인

    • JPA가 제공하는 PersistenceUnitUtil.isLoaded(Object entity) 메소드를 사용하면 프록시 인스턴스의 초기화 여부를 확인할 수 있다.
    • 조회한 엔티티가 진짜 엔티티인지 프록시로 조회한 것인지 확인하려면 클래스명을 직접 출력해보면 된다.

    8.2 즉시 로딩과 지연 로딩

    JPA는 개발자가 연관된 엔티티의 조회 시점을 선택할 수 있도록 두 가지 방법을 제공한다.

    • 즉시 로딩: 연관된 엔티티를 즉시 조회한다. 하이버네이트는 가능하면 SQL 조인을 사용해서 한 번에 조회한다.
      • 설정 방법: @ManyToOne(fetch = FetchType.EAGER)
    • 지연 로딩: 연관된 엔티티를 프록시로 조회한다. 프록시를 실제 사용할 때 초기화하면서 데이터베이스를 조회한다.
      • 설정 방법: @ManyToOne(fetch = FetchType.LAZY)

    8.3 지연 로딩 활용

    8.3.2 JPA 기본 페치 전략

    • JPA의 기본 페치(fetch) 전략은 연관된 엔티티가 하나면 즉시 로딩을, 컬렉션이면 지연 로딩을 사용한다.
    • 저자는 모든 연관관계에 지연 로딩을 사용하는 것을 추천한다. 그리고 필요한 곳에만 즉시 로딩을 사용하도록 최적화한다.

    8.3.3 컬렉션에 FetchType.EAGER 사용 시 주의점

    • 컬렉션을 하나 이상 즉시 로딩하는 것은 권장하지 않는다.
    • 컬렉션 즉시 로딩은 항상 외부 조인(OUTER JOIN)을 사용한다.

    FetchType.EAGER 설정과 조인 전략

    • @ManyToOne, @OneToOne
      • (optional = false): 내부 조인
      • (optional = true): 외부 조인
    • @OneToMany, @ManyToMany
      • (optional = false): 외부 조인
      • (optional = true): 외부 조인

    8.4 영속성 전이: CASCADE

    • 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶으면 영속성 전이(transitive persistence) 기능을 사용한다.
    • JPA는 CASCADE 옵션으로 영속성 전이를 제공한다. 영속성 전이를 사용하면 부모 엔티티를 저장할 때 자시 엔티티도 저장할 수 있다.
    • JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다.

    8.4.1 영속성 전이: 저장

    • 부모를 영속화할 때 cascade = CascadeType.PERSIT 옵션을 설정하면 부모와 자식 엔티티를 한 번에 영속화할 수 있다.
    • 영속성 전이는 연관관계를 매핑하는 것과는 관련 없이 엔티티를 영속화할 때 연관된 엔티티도 영속화하는 편리함을 제공할 뿐이다.

    8.4.2 영속성 전이: 삭제

    • CascadeType.REMOVE로 설정하고 부모 엔티티를 삭제하면 연관된 자식 엔티티도 삭제된다.
    • CascadeType.REMOVE를 설정하지 않고 부모 엔티티만 삭제하면 자식 테이블의 외래키 제약조건으로 인해 데이터베이스에서 예외가 발생한다.

    8.4.3 CASCADE의 종류

    public enum CascadeType {
        ALL,        // 모두 적용
        PERSIST,    // 영속
        MERGE,      // 병합
        REMOVE,     // 삭제
        REFRESH,    // REFRESH
        DETACH      // DETACH
    }

    8.5 고아 객체

    • 고아 객체(ORPHAN) 제거: JPA는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동을 삭제한다.
    • 고아 객체 제거는 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능이다. 따라서 참조하는 곳이 하나일 때만 사용해야 한다.
    • orphanRemovel은 @OneToOne, @OneToMany에만 사용할 수 있다.
    • 고아 객체 제거에서 부모를 제거하면 자식은 고아가 되어 자식이 같이 제거된 것(CascadeType.REMOVE를 설정한 것)과 같다.

    8.6 영속성 전이 + 고아 객체, 생명주기

    CascadeType.ALL + orphanRemoval = true를 동시에 사용하면 부모 엔티티를 통해 자식의 생명주기를 관리할 수 있다.

    • 자식을 저장하려면 부모에 등록만 하면 된다(CASCADE).
    • 자식을 삭제하려면 부모에서 제거하면 된다(orphanRemoval).

     

    ✔️ 복습하기
    1. em.find()와 em.getReference()의 차이점은?
    2. 영속성 컨텍스트에 이미 로딩된 엔티티가 있을 때 em.getReference()를 호출하면 무엇이 반환되는지?
    3. 모든 연관관계를 FetchType.LAZY(지연 로딩)으로 설정하는 것을 권장하는 이유는?
    4. CascadeType.PERSIST란?

     

    이 글은 『 자바 ORM 표준 JPA 프로그래밍』 책을 학습한 내용을 정리한 것입니다.

     

    Comments