프록시와 즉시로딩 및 지연로딩

최주영·2024년 12월 30일
0

JPA

목록 보기
7/7

✅ 프록시 객체 : 지연로딩 즉 엔티티의 연관된 데이터를 필요할 때만 로드하기 위해 사용

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


✅ 프록시 객체의 초기화

target에 값이 없음 -> jpa가 영속성 컨텍스트에 요청 -> 영속성 컨텍스트가 DB를 조회함
-> 실제 엔티티를 생성해서 줌 -> target이 생성한 엔티티를 연결함
-> 한번만 초기화하면 다시 DB를 조회할 필요 없음


✅ 프록시 객체의 특징

  • 프록시 객체는 실제 객체의 참조를 보관하며, 프록시 객체 호출 시 실제 객체의 메소드를 호출
  • 프록시 객체는 처음 사용할 때 한번만 초기화
  • 프록시 객체를 초기화할 때, 프록시 객체가 실제 엔티티로 바뀌는게 아니라
    초기화되면 프록시 객체를 통해서 실제 엔티티에 접근이 가능함
Member findMember = em.getReference(Member.class, member.getId());
System.out.println(findMember.getClass()); // Member 클래스가 아닌 프록시 클래스로 나옴
  • 프록시 객체는 원본 엔티티를 상속받기 때문에, 타입 체크시 유희해야함 = instance of 사용
Member m1 = em.find(Member.class, member.getId());
Member m2 = em.getReference(Member.class, member.getId());
System.out.println(m1.getClass() == m2.getClass()); // 타입이 다르기때문에 false  나옴
System.out.println(m1 instance of Member); // true
System.out.println(m2 instance of Member); // true
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면, em.getReference() 를 호출해도 실제 엔티티 반환

  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시 초기화시 문제발생
    -> 하이버네이트는 org.hibernate.LazyInitalizationException 예외를 터뜨림

Member member1 = new Member();
member1.setUsername("최최최");
em.persist(member1); // 영속성컨텍스트에 영속상태로 저장

em.flush(); // 영속성 컨테스트의 변경내용을 데이터베이스에 반영 (쿼리던짐)
em.clear();

Member refMember = em.getReference(Member.class, member1.getId());
em.clear(); // 준영속상태

refMember.getUsername(); // 오류발생

getReference : 진짜 객체를 주는게 아니라, 가짜(프록시) 엔티티 객체를 조회


✅ 지연로딩 : 조인된 클래스를 자주 조회하지 않을 경우 사용

public class Member{
	@Id
    @GeneratedValue
    private Long id;
    
    @Column(name = "USERNAME")
    private String name;
    
    @ManyToOne(fetch = FetchType.LAZY) // fetch = FetchType.LAZY 지연로딩 사용
    @JoinColumn(name = "TEAM_ID") 
    private Team team;
}

team을 지연로딩으로 사용했을 때, member을 찾았을 경우 Team이 조회되지 않는다
직접 team.getName() 과 같이 메소드를 호출 했을 때 DB 쿼리가 날라간다
이런식으로 지연로딩을 하면 member 만 사용하고싶을 때 굳이 team까지 찾는 쿼리를 날릴 필요가 없다

즉 쉽게말해서 지연로딩을 하면 조인된 클래스를 본인이 원하는 시점에서 조회할 수 있음


✅ 즉시로딩
위와 같이 반대로 Member 클래스를 사용할 때 Team 클래스도 자주 같이 사용하는 경우
fetch = FetchType.EAGER 을 사용한다
그러면 Member member = em.find(Member.class, 1L); 를 입력했을 때
Member를 찾을때부터 Team도 한번에 조인해서 조회할 수 있다


✅ 실무 팁

  • 실무에서는 가급적 지연로딩 만 사용하자 = 모든 연관관계는 지연로딩을 사용
    -> 즉시로딩을 적용하면 예상치 못한 SQL 발생
  • JPQL fetch 조인 + 엔티티 그래프 기능을 사용
  • @ManyToOne OneToOne -> 기본이 즉시 로딩으로 LAZY 로 설정해야함
  • @OneToMany ManyToMany -> 기본이 지연로딩임

✅ 영속성 전이 CASCADE

  • 특정 엔티티를 영속상태로 만들 때, 연관된 엔티티도 함께 영속상태로 만들고 싶을 때 사용
  • 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장
  • 엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하려는 편리함을 제공할 뿐
    연관관계를 매핑하는 것과는 상관 없음
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)

✅ 고아 객체 제거

  • 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제
  • 참조하는곳이 하나일 때 사용함 = 특정 엔티티가 개인 소유할 때 사용
  • 부모를 제거하면 자식도 함께 제거됨 -> CascadeType.REMOVE 처럼 동작함
  • orphanRemoval = true
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거

-> 그후에 delete 쿼리가 나감
profile
우측 상단 햇님모양 클릭하셔서 무조건 야간모드로 봐주세요!!

0개의 댓글