본문 바로가기
Jpa

프록시와 연관관계

by k0o9 2021. 1. 10.

참고 이글은 자바 ORM 표준 JPA프로그래밍을 읽고 정리한 글입니다.

 

프록시

엔티티를 조회할 때 연관된 엔티티들이 항상 사용되는 것이 아님.

@Entity
class Member{
    @Id
    private Long id;

    // ...

    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
}

@Entity
class Team{
    @Id
    private Long id;

    // ...
}

맴버를 조회하면 항상 team을 같이 조회하게 된다. 이렇게 되면 효율적이지 않다.

위와같은 문제를 해결하기 위해 엔티티가 실제 사용될 때까지 데이터베이스 조회르르 지연하는 방법을 제공하는데 이것을 지연 로딩이라 한다. 

이러한 지연로딩을 사용하려면 실제 엔티티 객체 대신에 데이터 베이스 조회를 지연할 수 있는 가짜 객체가 필요한데 이것을 프록시 객체라 한다.

 

프록시 기초

jpa 엔티티 조회 순서

1.EntityManager.finde()를 사용

2.영속성 컨텍스트에 엔티티 조회

3.2번에서 없을경우 DB 조회

이렇게 엔티티를 조회하면 엔티티를 사용하지 않아도 DB에 조회하게 된다. 엔티티를 실제 사용하는 시점까지 DB조회를 미루고 싶으면 EntityManager.getReference()를 사용하면 된다.

저 메소드를 실행하면 데이터베이스 접근을 위한 프록시 객체를 반환한다.

 

em.find() vs em.getReference()

em.find()=실제 엔티티 객체 조회

em.getReference()=DB조회를 미루는 프록시 객체 조회

 

-프록시의 특징

:사용자 입장에서 진짜 객체인지 프록시객체인지 구분하지 않고 사용하면 된다.

:프록시 객체는 실제 객체에 대한 참조를 보관

:프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출.

 

-프록시 객체 초기화

:실제 사용될때 DB를 조회해서 실제 엔티티 객체를 생성.

class MemberProxy extends Member{
    Member target = null;

    public String getName(){
        if(target == null){
        	// 초기화요청
            // DB 조회  
            // 실제 엔티티 생성 및 참조 보관  
            this.target = // ...
        }
		//target.getName();
        return this.target.getName();  
    }
}

프록시 객체

-초기화 과정 분석

1.프록시 객체에 member.getName() 호출해서 실제 데이터 조회

2.실제 엔티티가 생성되어 있지 않으면 영속성 컨텍스트에 실제 엔티티 생성을 요청 -> 초기화

3.영속성 컨텍스트는 DB를 조회해서 실제 엔티티 객체 생성

4.프록시 객체는 생성딘 실제 엔티티 객체의 참조를 맴버변수에 보관.

5.실제 엔티티 객체의 getName()호출해서 결과 반환

 

-특징

: 처음 사용할 때 한번만 초기화

: 초기화한다고 실제 객체가 바뀌는 것은 아님. 초기화 되면 실제 엔티티에 접근이 가능해지는것.

: 원본 엔티티에 상속받은 객체이므로 타입체크 시에 주의

: 이미 있으면 db 조회 x -> getReference()를 호출해도 프록시가 아닌 실제 엔티티 반환

: 영속성 컨텍스트의 도움이 있어야만 초기화 가능 -> 준영속 상태의 프록시 초기화하면 에러.

 

프록시와 식별자

PersistenceUnitUtil.isLoaded(Object entity) -> 프록시 인스턴스 초기화 여부(초기화x이면 false)

 

entity.getClass().getName() 출력(..javasist.. or HibernateProxy…) -> 프록시 클래스 확인

 

org.hibernate.Hibernate.initialize(entity);->프록시 강제 초기화

 

 

즉시로딩과 지연로딩

프록시 객체는 주로 연관된 엔티티를 지연 로딩할 때 사용한다.

 

- 즉시 로딩

: 엔티티를 조회할 때 연관된 엔티티도 함께 조회한다.

 

-지연 로딩

: 연관된 엔티티르르 실제 사용할 때 조회한다.

 

즉시 로딩

@ManyToOne의 fetch속성을 FetcgType.EAGER로 지정한다.

엔티티를 조회하면 그 엔티티가 참조하고있는 다른 엔티티도 즉시 로딩한다.

jap구현체는 즉시로딩을 최적화하기 위해 가능하면 조인쿼리로 한번에 사용한다.

 

 

지연로딩

@ManyToOne의 fetch속성을 FetcgType.Lazy로 지정한다.

실제 엔티티를 사용할때 까지 로딩하지 않는다.

실제 데이터가 필요한 순간이 되어서야 DB를 조회해서 프록시 객체르르 초기화.

 

 

지연로딩 활용

 

 

사내 주문 관리 시스템 모델이 이렇게 되이삳고 보자. 애플리케이션 로직을 분석하니 다음과 같았다.

 

Member와 연관된 Team은 자주 함께 사용 -> 즉시로딩

Member와 연관된 Order는 가끔 사용 -> 지연로딩

Order와 Product는 자주 함께 사용 ->즉시 로딩

 

member를 조회할 경우 다음과 같다.

team은 즉시 로딩이므로 바로 로딩하고 주문내역은 지연로딩이므로 실제로 쓰일때까지 로딩을 하지 않는다.

 

프록시와 컬렉션 래퍼

컬렉션 래퍼?

: 하이버네이트는 엔티티를 영속 상태로 만들때 엔티티에 컬렉션이 있으면 컬렉션을 추적하고 관리할 목적으로 원본 컬렉션을 하이버네이트가 제공하는 내장 컬렉션으로 변경.

 

member.getOrders()를 호출해도 컬렉션은 초기화되지 않는다. 컬렉션은 실제 데이터를 조회할 때 DB를 조회해서 초기화 된다. -> member.getOrders().get(0)호출시 초기화. 주문내역이 초기화되면서 주문내역에 즉시로딩 관계인 상품도 함께 로딩된다.

 

JPA 기본 페치 전략

@ManyToOne @OneToOne -> 즉시 로딩

@OneToMany @ManyToMany -> 지연 로딩

연관된 엔티티가 하나면 즉시로딩, 여러개면 지연로딩 

 

*추천 방법 : 모든 연관관계에 지연로딩 사용. 그리고 완료 단계에서 최적화하며 필요한곳만 즉시로딩.

 

영속성 전이:CASCADE

특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을때 사용. JPA는 CASCADE옵션으로 영속성 전이를 제공한다. 쉽게 말해 부모 엔티티를 저장할때 자식 엔티티도 함께 저장할 수 있다.
Jpa에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다.

 

영속성 전이 : 저장

영속성 전이 저장

부모클래스에 @OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST) 추가 할경우 부모만 영속화 하면 자식도 함께 영속해서 저장.

영속성 전이는 연관관계 매핑과 상관없이 엔티티를 영속화할 때 연관된 엔티티도 같이 영속화하는 편리함을 제공할 뿐이다.

 

CASCADE 종류

ALL: 모두 적용

PERSIST: 영속

REMOVE: 삭제

MERGE: 병합 

REFRESH: REFRESH 

DETACH: DETACH

참고로 영속성 persist,remove를 실행할 때 바로 전이가 발생하지 않고 플러시를 호출 할 때 전이가 발생한다.

 

고아객체제거

JPA가 지원하는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기술. 

컬렉션에 @OneToMany 속성orphanRemoval = true 로 설정.

고아 객체 제거 기능은 영속성 컨텍스트를 플러시 할때 적용되므로 플러시 시점에 DELETE SQL문 실행.

 

고아객체는 참조하는 곳이 하나일 때만 사용해야 한다. -> @OneToOne이나 @OneToMany 일때만 사용가능.

 

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

일반적으로 엔티티는 persist로 영속화 remove를 통해 제거 된다. 

->이것은 엔티티 스스로 생명주기를 관리한다는 뜻.

CascadeType.ALL + orphanRemovel=true

옵션을 활성화하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.

즉 자식을 저장하면 부모에 등록되고 자식을 제거하면 부모에서 제거된다.

 

 

 

 

 

 

 

 

 

 

 

 

'Jpa' 카테고리의 다른 글

객체 지향 쿼리 언어  (0) 2021.01.13
값 타입  (0) 2021.01.11
고급매핑  (0) 2021.01.10
다양한 연관관계 매핑  (0) 2021.01.10
연관관계 기초 매핑  (0) 2021.01.09