가자공부하러!

Hibernate(6) - 프록시, 즉시/지연 로딩, 연관관계 관리 본문

공부/Java

Hibernate(6) - 프록시, 즉시/지연 로딩, 연관관계 관리

오피스엑소더스 2019. 11. 22. 10:42

1. 프록시

  > em.getReference() : DB조회를 미루는 가짜(프록시) 엔티티 객체 조회

    - DB에 쿼리가 안나가는데 객체가 조회됨 -> 객체값이 실제 사용되는 시점에 DB에 쿼리를 날림

    - class domain.Member$HibernateProxy$ojP0rbKz

  > 사용 목적 

    - 엔티티와 연결된 모든 정보를 가져올 필요 없이 일부만 활용하고 싶을 때

      - ex) Member를 조회할 때 Team은 조회하고 싶지 않다.

    - 지연로딩

  > 특징

    - 프록시는 실제 클래스를 상속 받아서 만들어지므로 실제 클래스와 겉 모양이 같다.

    - 이론상 사용하는 입장에서 진짜 객체인지 프록시 객체인지 구분할 필요가 없다.

    - 프록시 객체는 최초 1회만 초기화되며, 실제 객체의 참조를(target) 보관

    - 프록시 객체는 원본 엔티티를 상속받기 때문에 타입 체크 시 주의 필요

      - ==비교 말고 instanceof 사용해야 함

    - 영속성컨텍스트에 getReference()로 찾는 객체가 이미 있다면 실제 엔티티 리턴

      - 실제 객체가 영속성 컨텍스트의 1차 캐시에 저장되어 있는 경우

      - 그 반대도 성립 : 프록시객체가 영속성 컨텍스트에 있을 때 find 하면 프록시 객체 리턴

    - 준영속상태일 때 프록시를 초기화하면 문제 발생

  > 작동

    - 순서 :

      -> 프록시객체의 메소드 호출 -> 프록시객체가 영속성컨텍스트에게 초기화 요청

      -> 영속성 컨텍스트가 DB조회 -> 실제 엔티티 생성

      -> 프록시객체가 실제 엔티티(target)의 값을 가져다가 리턴

      * 초기화 시점 : 프록시 객체가 실제 사용될 때(getter, setter 등)

  > 응용

    - 프록시 인스턴스의 초기화 여부 확인하는 메소드

      - boolean emf.getPersistanceUnitUtil.isLoaded(Object Entity);

    - 프록시 클래스 확인 메소드

      - String entity.getClass().getName();

    - 프록시 강제 초기화 메소드

      - org.hibernate.Hibernate.initialize(Object Entity)

 

2. 즉시 로딩과 지연 로딩

  > 지연 로딩

    - @ManyToOne(fetch = FetchType.LAZY)를 연관 엔티티 변수 위에 선언

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;

    - 이 때 Member에서 Team을 조회하게되면 프록시객체가 생성된다.

  > 즉시 로딩

    - @ManyToOne(fetch = FetchType.EAGER)를 연관 엔티티 변수 위에 선언

    - 한 번에 연관 엔티티까지 모두 가져오는 쿼리가 실행됨

  > 가능하면 지연 로딩만 사용하는게 좋다.

    - JPQL로 지연 로딩으로 선언된 내용을 상황에 맞게 즉시 로딩처럼 사용할 수 있다.

      - em.createQuery("select m from Member m join fetch m.team, Member.class);

    - 즉시 로딩을 적용하면 전혀 예상하지 못한 SQL이 만들어진다.

    - 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.(내가 날린 쿼리는 1개인데 쿼리 N개가 더 수행됨)

      - Member만 가지고 온 상황에 FetchType.EAGER를 확인하고 또 다시 Team정보를 가져오게 된다.

    - @ManyToOne, @OneToOne은 디폴트가 즉시 로딩이기 때문에 LAZY를 별도로 설정해 주어야 한다.

    - @OneToMany, @ManyToMany는 디폴트가 지연로딩

 

3. 영속성 전이(CASCADE)

  > 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때

  > 연관관계를 매핑하는 것과는 전혀 관련 없음

  > @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)

    - ALL, PERSIST, REMOVE, MERGE, REFRESH, DETACH

  > 주의 : 하위 엔티티가 다른 엔티티들과 연관관계가 있는 경우에는 사용하지 말것

 

4. 고아 객체

  > 부모 엔티티와 연관관계가 끊어진 자식 엔티티

  > @OneToMany(orphanRemoval = true)

@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> children = new ArrayList<Child>();

  > 위처럼 설정했을 때, children 리스트에서 특정 Child 객체를 remove하면 delete쿼리를 수행한다.

  > 주의 : 충돌을 예방하기 위해서는 참조하는 곳이 하나일 때만 사용해야 함

  > 주의 : 부모를 삭제하면 자식이 고아가되므로 orphanRemoval옵션일 때에는 자식도 삭제된다.

 

5. 영속성 전이 + 고아 객체

  > 도메인 주도 설계(DDD)의 Aggregate Root 개념을 구현할 때 유용

  > @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)

  > 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.

 

 

Comments