[24.09.17] TIL

yy·2024년 9월 17일

개발일지

목록 보기
106/122

java jpa 공부중

프록시

  • Member의 속성만을 조회할 때 Team의 내용까지 조회를 해야할까? 아니오.

  • em.find() : DB를 통해서 실제 엔티티 객체 조회

  • em.getReference() : DB조회를 미루는 가짜(프록시) 엔티티 객체 조회.
    : DB에 쿼리가 안나갔는데 쿼리가 조회가 되는..?

  • em.find()를 하면 DB에 쿼리를 날려 값을 가져오지만 em.getReference()의 경우 메소드가 실행될 때는 DB에 쿼리를 날리지 않는다.


프록시의 특징

프록시 객체는 처음 사용할 때 한 번만 초기화
프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님. 초기화되면 프록시 객체를 통해서 실제 엔티티 접근이 가능
프록시 객체는 원본 엔티티를 상속받음. 따라서 타입 체크시 주의해야함( == 비교 X, 대신 instance of 사용)
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환
영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생 (하이버네이트는 org.hibernate.LazyInitializationException 예를 터트림)


즉시로딩과 지연로딩

  • 지연로딩 : LAZY을 사용해서 프록시로 조회
@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY) //LAZY 로딩
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ...
}
Member member = em.find(Member.class, 1L);
Team team = member.getTeam();
team.getName(); //실제 team을 사용하는 시점에 초기화(DI)

  • 즉시로딩 : EAGER을 사용해서 함께 조회
@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.EAGER) //즉시로딩
    
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    ...
}
Member member = em.find(Member.class, 1L);
Team team = member.getTeam();
team.getName(); //em.find()로 조회할 때부터 한번에 조회됨
  • 즉시로딩 : Member 조회 시 항상 Team도 같이 조회. JPA 구현체는 가능하면 조인을 사용해서 SQL한번에 함께 조회.
  • 가급적 지연 로딩만 사용(특히 실무에서)
  • 즉시 로딩을 적용하면 예상치못한 SQL발생
  • 즉시 로딩은 JPQL에서 N+1문제를 일으킴
  • @ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 설정
  • @OneToMany, @ManyToMany는 기본이 지연로딩

지연로딩 활용

  • Member, Team은 자주 함께 사용 -> 즉시로딩
  • Member, Order는 가끔 사용 -> 지연 로딩
  • Order와 Product는 자주 함께 사용 -> 즉시 로딩

=> 지연 로딩 활용

  • 모든 연관관계에 지연 로딩 사용
  • 실무에서 즉시 로딩 사용X
  • JPQL fetch 조인이나, 엔티티 그래프 기능 사용
  • 즉시 로딩은 상상하지 못하는 쿼리 발생

영속성 전이 (CASCADE)

  • 특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속상태로 만들고 싶을 때
    (예) 부모 엔티티 저장하고 자식 엔티티에서도 저장하고 싶을 때
  • parent한테 persist를 날려도 child한테도 같이 persist를 날려줄거야 하는 연쇄성을 가진 옵션이 cascade
public class Parent {
    ...
    @OneToMany(mappedBy= "parent", cascade= CascadeType.ALL)
    private List<Child> childList = new ArrayList<>();
    ...
}

주의점

  • 영속성 전이는 연관관계를 매핑하는 것과 아무관련이 없음
  • 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐임
  • 언제 쓰는가 : 하나의 부모가 자식들을 완전히 관리할 때 의미가 있음. 다른 테이블과 자식들과 연관이 있다면 안쓰는게 좋음.

CASCADE 옵션

  • ALL : 모두 적용
  • PERSIST: 영속 (저장할때만)
  • REMOVE : 삭제

고아객체

  • 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
  • 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능
  • 참조하는 곳이 하나일 때 사용해야함.
  • 특정 엔티티가 개인 소요할 때 사용
  • @OneToOne, @OneToMany만 가능
  • 개념적으로 부모를 제거하면 자식은 고아가 됨. 따라서 고아 객체 제거기능을 활성화하면 부모를 제거할 때 자식도 함께 제거됨. CascadeType.REMOVE처럼 동작함.
@OneToMany(orphanRemoval = true) //하면 

Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0);// 자식 엔티티를 컬렉션에서 제거

영속성 전이 + 고아 객체, 생명 주기 (CascadeType.ALL + orphanRemoval = true)

  • 스스로 생명주기를 관리하는 엔티티는 em.persist()로영속화, em.remove()로 제거
  • 두 옵션을 모두 활성화하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있음
  • 도메인 주도 설계(DDD)의 Aggregate Root개념을 구현할 때 유용
profile
시간이 걸릴 뿐 내가 못할 건 없다.

0개의 댓글