✍프록시 기초
- em.find(): 데이터베이스를 통해 실제 Entity 객체 조회.
- em.getReference() : 데이터베이스 조회를 미루는 가짜(프록시) Entity 객체 조회.
=> getReference()를 통해 가져온 객체는 ORM이 만들어낸 프록시엔티티 객체이다. 실제 데이터가 필요한 시점이 오면 DB에 쿼리를 날린다.
💡 프록시 특징
- 실제 클래스를 상속 받아서 만들어진다.
- 실제 클래스와 겉 모양이 같다. (내부 데이터는 비어있다.)
- 사용하는 입장에서는 이론상 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.
- 프록시 객체는 실제 객체의 참조(target)를 보관한다.
출처 - https://www.inflearn.com/course/ORM-JPA-Basic/dashboard
- 프록시 객체는 처음 한번만 초기화.
- 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것이 아님.
- 프록시 객체는 원본 엔티티를 상속받기때문에 엔티티를 비교할 때, instanceof 를 사용해야한다.
- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 getReference()를 호출해도 프록시 객체가 아닌, 실제 Entity 객체가 반환됨.
(동일한 트랜잭션 내에서 같은 PK로 불러온 같은 Entity라면 '==' 연산자에 대해 항상 True를 보장해주기 때문이다.)
따라서, 영속성 컨텍스트에 프록시가 이미 있고 find()를 호출하면 Entity 객체가 아닌, 프록시 객체가 반환된다. - 준영속 상태일 때, 프록시를 초기화 하면 문제 발생 ! (org.hibernate.LazyInitializationException 예외 발생)
🐱🏍 지연 로딩
- 연관 관계 매핑 어노테이션에 fetch = FetchType.LAZY 속성을 추가.
@ManyToOne(fetch = FetchType.LAZY)
- Entity를 조회 할 때, 연관 관계에 있는 Entity는 프록시로 대체함.
- 이후, 연관 관계에 있는 Entity의 데이터에 접근하려 할 때, 프록시가 초기화 됨. (실제 값이 필요할 때 쿼리 발생)
해결 방법
=> FetchJoin을 사용하여 필요할 때만 연관된 Entity를 가져올 수 있다.
=> 엔티티 그래프 기능
=> batch size
🚴♂️ 즉시 로딩
- 연관 관계 매핑 어노테이션에 fetch = FetchType.EAGLE 속성을 추가.
- 이론적으로 연관된 테이블을 자주 함께 사용하면 즉시 로딩을 사용한다.
- Entity를 조회 할 때, 연관 관계에 있는 Entity도 함께 조회.
- JPA 구현체는 가능하면 조인을 사용하여 SQL 한번에 함께 조회.
🧨실무에서 가급적 지연 로딩 사용.
- 즉시 로딩을 적용하면 예상치 못한 SQL이 발생. (연관된 Entity도 무조건 함께 조회하기 때문.)
- 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다. 쿼리가 N개씩 추가적으로 발생.
- @ManyToOne, @OneToOne 은 default가 즉시 로딩 -> LAZY 로 설정하자.
CASCADE - 영속성 전이
- 특정 엔티티를 영속 상태로 만들 때, 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 사용.
- 연관 관계 매핑 어노테이션에 cascade = CascadeType.ALL 으로 지정
- 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없다.
- 연관된 Entity의 생명주기가 완전히 같아야 할 때, 연관된 Entity의 소유자가 하나일 때 사용하자.
=> Entity 가 다른 테이블과도 연관관계가 있을 때 사용하면 로직이 복잡해짐.
- ALL : 모두 적용
- PERSIST : 영속
- REMOVE : 삭제
- MERGE : 병합
- REFRESH : REFRESH
- DETACH : DETACH
고아 객체
- 특정 엔티티에서 연관 관계에 있는 엔티티를 지울 때(상위 엔티티와 하위 엔티티의 관계가 끊어질 때) 해당 엔티티를 지움(DELETE 쿼리 발생)
- 연관 관계 매핑 어노테이션에 orphanRemoval = true 으로 지정
- 특정 엔티티가 개인 소유할 때 사용. (다른 엔티티와 연관관계가 없을 때)
- OneToOne, OneToMany에서 사용.
영속성 전이 + 고아 객체
- CascadeType.ALL + orphanRemoval = true
- 스스로 생명 주기를 관리하는 엔티티는 persist(), remove()로 영속화, 제거.
- 두 옵션을 모두 활성화하면 상위 엔티티(OneToMany)를 통해 하위 엔티티의 생명 주기를 관리할 수 있음.
(상위 엔티티를 persist() 하면 하위 엔티티의 List가 자동 저장 / 상위 엔티티에서 하위 엔티티의 List를 지우면 하위 엔티티의 데이터가 DB에서 DELETE 됨. -> JPA가 관리)
본 게시글은 김영한님의 Inflearn 강의를 토대로 제작되었습니다.