Chapter8. 프록시와 연관관계 관리

김신영·2023년 2월 20일
0

JPA

목록 보기
6/14
post-thumbnail

Proxy

  • 지연 로딩 기능을 지원하는데 사용되는 가짜 객체
  • 지연 로딩 기능을 구현하는 방법은 크게 2가지
    1. 프록시 객체를 사용하는 방법
    2. 바이트코드를 수정하는 방법

프록시 객체 조회 예시 코드

Member member = em.getReference(Member.class, 1L);
member.getName();	// 1. getName();

프록시의 특징

  • 프록시 객체는 처음 사용할 때 한 번만 초기화
  • ⚠️ 프록시 객체는 원본 엔티티를 상속받은 객체이므로, 타입 체크시 주의
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면, 데이터베이스를 조회할 필요가 없다.
    • 따라서, em.getReference(...) 를 호출해도 프록시가 아닌 실제 엔티티를 반환
  • 🚨 준영속 상태의 프록시를 초기화하면, org.hibernate.LazyInitializationException 예외 발생

프록시 확인

  • PersistenceUnitUtil.isLoaded(Object entity)
  • Hibernate.isInitialized(Object entity)
boolean isLoaded = em.getEntityManagerFactory()
	.getPersistenceUnitUtil()
	.isLoaded(entity);

boolean isLoaded2 = Hibernate.isInitialized(entity);

프록시 강제 초기화

  • Hibernate.initialize(Object entity)

즉시 로딩과 지연 로딩

  • 즉시 로딩
    • 엔티티를 조회할 때 연관된 엔티티도 함께 조회한다.
    • @ManyToOne(fetch = FetchType.EAGER)
  • 지연 로딩
    • 연관된 엔티티를 실제 사용할 때 조회한다.
    • @ManyToOne(fetch = FetchType.LAZY)

즉시 로딩

  • @JoinColumn(nullable = true)
    • 기본 설정
    • NULL 허용
    • 외부 조인 사용
  • @ManyToOne(fetch = FetchType.EAGER, optional = true)
    • 기본 설정
    • NULL 허용
    • 외부 조인 사용
  • @JoinColumn(nullable = false)
    • NULL 허용하지 않음
    • 내부 조인 사용
  • @ManyToOne(fetch = FetchType.EAGER, optional = false)
    • NULL 허용하지 않음
    • 내부 조인 사용

JPA 기본 Fetch 전략

  • @ManyToOne, @OneToOne
    • 즉시 로딩 (기본 값)
  • OneToMany, ManyToMany
    • 지연 로딩 (기본 값)

영속성 전이 (CASCADE)

  • 특정 엔티티를 영속 상태로 만들 때, 연관된 엔티티도 함께 영속 상태로 만드는 기능
    • 생명주기를 특정 엔티티와 동일하게 하고 싶을 때!
  • 엔티티를 영속화할 때 연관된 엔티티도 같이 영속화하는 편리함을 제공

⚠️ JPA에서 엔티티를 저장할 때, 연관된 모든 엔티티는 영속 상태이어야 한다!

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

    private String name;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
    private List<Child> childeren = new ArrayList<>();
}

Child child1 = new Child();
Child child2 = new Child();

Parent parent = new Parent();
child1.setParent(parent); // 연관 관계 추가
child2.setParent(parent); // 연관 관계 추가
parent.getChilderen().addchild(child1); 
parent.getChilderen().addchild(child2); 

em.persist(parent);

![https://velog.velcdn.com/images%2Fljinsk3%2Fpost%2F3bafeed4-4595-40d6-abd0-aa42ec4c387c%2Fimage.png%5D(https%3A%2F%2Fimages.velog.io%2Fimages%2Fljinsk3%2Fpost%2F3bafeed4-4595-40d6-abd0-aa42ec4c387c%2Fimage.png)

CASCADE 종류

public enum CascadeType { 
    ALL,      // 모두 적용

    PERSIST,  // 영속

    MERGE,    // 병합

    REMOVE,   // 삭제

    REFRESH,  // REFRESH

    DETACH.   // DETACH
}

고아 객체 (Orphan)

  • 고아 객체 제거
    • 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능
  • 부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면
    • 자식 엔티티가 자동으로 삭제됨
@Getter
@Entity
public class Parent {
    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "parent", orpahnRemoval = true)
    private List<Child> childeren = new ArrayList<>();
}

Parent parent = em.find(Parent.class, 1L);
parent.getChilderen().remove(0);  // 자식 엔티티를 컬렉션에서 제거

// DELETE FROM CHILD WHERE ID=?
  • 고아 객체 제거 기능은 영속성 컨텍스트를 플러시할 때 적용된다.

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

  • 자식을 저장하려면 부모에 등록만 하면 된다. (CASECADE)
Parent parent = em.find(Parent.class, 1L);
parent.getChildren().add(child);
  • 자식을 제거하려면 부모에서 제거하면 된다. (orphanRemoval)
Parent parent = em.find(Parent.class, 1L);
parent.getChildren().remove(child);
profile
Hello velog!

0개의 댓글