UmaiCo - 우마이코

[JPA] 7. 프록시와 연관관계 관리 본문

JPA/JPA Basic

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

코드 미식가 2023. 1. 31. 00:07

프록시와 연관관계 관리

 

목차

  • 프록시
  • 즉시 로딩과 지연로딩
  • 지연 로딩 활용
  • 영속성 전이: CASCADE
  • 고아 객체
  • 영속성 전이 + 고아 객체, 생명주기

 

1. 프록시

 

  • Member와 Team 객체가 있다.
  • Member와 Team 은 다대일 양방향 관계이다.
  • em.find(Member.Class(), member.getId());
  • 을 실행하면 Member객체와 Team 객체를 한번에 가져온다.
  • Member와 Team을 거의 같이 사용한다면 문제가 되지 않지만, Member객체만 필요한 상황에서 Team 객체까지 Join하여 가져오는 것은 낭비

 

프록시 기초

  • em.find() VS em.getReference()
  • em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회
  • em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 조회

프록시 객체의 초기화

Member member = em.getReference(Member.class, “id1”); 
member.getName();
  • getReference를 실해하면 member에 프록시 객체가 들어오고, 실제 member를 사용하는 getName()을 호출할때, 프록시 객체가 초기화 된다.
  • 초기화란 프록시 객체가 영속성 컨텍스트에 초기화 요청을 보내고 영속성 컨텍스트가 이때 DB를 조회하여 실제 member 객체를 생성하면 프록시 객체의 target이 생성된 member객체를 가지고 있는 형식이다.



프록시 특징

  • 실제 클래스를 상속받아서 만들어짐
  • 따라서 타입 체크시 (==비교 실패, 대신 instance of 사용)
  • 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용
  • 프록시 객체는 실제 객체의 참조(target)을 보관
  • 프록시 객체를 호출(초기화)하면 프록시 객체를 실제 객체의 메소드를 호출
  • 프록시 객체는 처음 사용할 때 한번 만 초기화
  • 프록시 객체를 초기화 할때, 프록시 객체가 실제 엔티티로 바뀌는 것이 아님. 프록시 객체를 통해 실제 엔티티에 접근하는 것
  • JPA의 트랜잭션 내부에서 같은 엔티티 반환(동일성 보장)
    • 영속성 컨텍스트에 실제 엔티티가 들어가 있으면 em.getReference()를 해도 실제 엔티티 반환
    • 영속성 컨텍스트에 프록시 객체가 들어가 있으면 em.find()를 해도 프록시 객체 반환
  • 준영속 상태일때 프록시를 초기화하면 오류 발생
    • 프록시 객체는 영속성 컨텍스트를 이용하여 초기화를 진행하기 때문

 

프록시 확인

  • 프록시 인스턴스의 초기화 여부 확인(target)
    • PersistenceUnitUtil.isLoaded(Object entity)
  • 프록시 강제 초기화
    • 실제 엔티티를 사용 (getName)
    • Hibernate.initialize(entity); -> 하이버네이트가 지원하는 기능
  • 참고: JPA 표준은 강제 초기화 기능 없음
    • member.getName()을 호출해야 함

 

2. 즉시 로딩과 지연로딩

사실 getReference() 조회방식은 잘 사용하지 않음. 하지만 지연로딩의 방식에서 프록시 객체를 사용

 

지연 로딩 LAZY을 사용해서 프록시로 조회

@ManyToOne(fetch = FetchType.LAZY) //**
@JoinColumn(name = "TEAM_ID")
private Team team;
Member member = em.find(Member.class, 1L); //member객체는 DB에서 찾아옴
Team team = member.getTeam(); // 프록시 객체
team.getName(); // 실제 team을 사용하는 시점에 초기화(DB 조회)

 

즉시 로딩 EAGER를 사용해서 함께 조회

@ManyToOne(fetch = FetchType.EAGER) //**
@JoinColumn(name = "TEAM_ID")
private Team team;
  • Member 조회시 항상 Team도 JOIN SQL로 한번에 조회

즉시 로딩 주의

  • 실무에서는 지연 로딩만 사용
  • 즉시 로딩을 적용하면 예상치 못한 SQL 반생
  • 즉시 로딩은 JPQL에서 N + 1 문제 발생
  • @ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 변경
  • @OneToMany, @ManyToMany는 기본이 지연 로딩

 

3. 지연 로딩 활용

 

  • 모든 연관관계에 지연 로딩적용
  • 즉시 로딩이 꼭필요한 경우에는 JPQL fech조인이나, 엔티티 그래프 기능 활용
  • 즉시 로딩은 상상하지 못한 쿼리가 나감

 

4. 영속성 전이: CASCADE

  • 특정 엔티티를 영속 상태로 만들때 연관된 엔티티도 함께 영속 상태로 만들고 싶을때 사용
  • ex) 부모 엔티티를 저장할때 자식 엔티티도 함께 저징

 

영속성 전이 - 주의

  • 영속성 전이는 연관관계를 매핑하는 것과 연관이 없음
  • 편리함만 제공

 

CASCADE 종류

  • ALL: 모두 적용
  • PERSIST: 영속
  • REMOVE: 삭제
  • MERGE: 병합
  • REFRESH: REFRESH
  • DETACH: DETACH

 

5. 고아 객체

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

orphanRemoval = true

  • 고아객체 제거: 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동 삭제
Parent parent1 = em.find(Parent.class, id); 
parent1.getChildren().remove(0);
//자식 엔티티를 컬렉션에서 제거

이러면 자식 Table에서도 삭제됨

 

주의

  • 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아객체로 인식하여 삭제함
  • 참조하는 곳이 하나일때 사용
  • 특정 엔티티가 개인 소유할 때 사용
  • @OneToOne, @OneToMany만 가능
  • 참고: 부모를 제거하면 자식은 고아가 된다. 따라서 이 기능을 사용하면 부모를 제거하면 자식도 함께 제거된다.(CascadeType.REMOVE처럼 동작)

 

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

  • CascadeType.ALL + orphanRemovel=true
  • 스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()롤 제거
  • 두 옵션을 모두 활성화 하면 부모 엔티티를 통해서 자식 엔티티의 생명주기 관리 가능
  • 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 때 유용

 

'JPA > JPA Basic' 카테고리의 다른 글

[JPA] 10. 객체지향 쿼리 언어2  (0) 2023.02.04
[JPA] 9. 객체지향 쿼리 언어1  (0) 2023.02.03
[JPA] 8. 값 타입  (0) 2023.01.31
[JPA] 6. 고급 매핑  (0) 2023.01.29
[JPA] 5. 다양한 연관관계 매핑  (0) 2023.01.29
[JPA] 참고. @JoinColumn  (0) 2023.01.29
[JPA] 참고. Entity, DAO, DTO, VO  (0) 2023.01.27
[JPA] 에러. SQL Error 23505  (0) 2023.01.27