우리는 공부할 때 이걸 "왜 써야하지"를 항상 의문으로 가지자.
em.find()
: 데이터베이스를 통해 실제 엔티티 객체를 조회
em.getReference()
: 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회 = DB에 쿼리는 안 나가는데 객체가 조회되는
==
대신 instanceof
를 사용하자 (프록시가 아닌 멤버랑 프록시 멤버랑 타입이 맞지 않기 때문)em.getReference()
를 호출해도 실제 엔티티를 반환한다.프록시 인스턴스의 초기화 여부 확인
➡️ PersistenceUnitUtil.isLoaded(Object entity)
프록시 클래스 확인 방법
➡️ entity.getClass.getName() 출력
프록시 강제 초기화
➡️ org.hibernate.Hibernate.initialize(entity);
JPA 표준은 강제 초기화 없음
강제 호출: member.getName()
🤷🏻♀️ Member를 조회할 때 Team도 조회해야 하나
단순히 member 정보만 사용하는 비즈니스 로직에서 member를 조회했는데 team까지 모두 조회되면 손해이다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
// 이렇게 false로 넣어주면 충돌이 발생하지 않고 읽기 전용이 됨
private Team team;
이렇게 fetch = FetchType.LAZY
해주면 프록시 객체로 조회가 된다.
🤷🏻♀️ 그런데 Member와 Team을 자주 같이 조회하게 된다면
@ManyToOne(fetch = FetchType.EAGE)
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
// 이렇게 false로 넣어주면 충돌이 발생하지 않고 읽기 전용이 됨
private Team team;
fetch = FetchType.EAGE
바로 결론을 얘기하자면 즉시 로딩은 사용하지 말자
- 우선, 실무에선 가급적 지연로딩만!! 사용하자.
(find할 때 다~~~ 끌려나오기 때문에 테이블이 10개쯤 되는 규모가 좀 있는 프로젝트라면 어우,,)- 즉시로딩은 JPQL에서 N+1문제를 일으킨다.
@ManyToOne
,@OneToOne
은 기본이 즉시로딩 → LAZY@OneToMany
,@ManyToMany
는 기본이 지연로딩
실무에선 무조건 지연 로딩 사용. 아래는 이론적으로..
📌 모든 연관관계에 지연 로딩을 사용하고 실무에서 즉시 로딩을 사용하지 말자!!
(즉시 로딩은 상상치 못한 쿼리가 발생)
JPQL fetch 조인이나 엔티티 그래프 기능을 사용하자 (추후 설명해주신다고 하심)
@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
📌 주의할 점
- 영속성 전이는 연관관계를 매핑하는 것과 아무 관련이 없으며
엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐 그 이상, 그 이하도 아니다.
ALL
: 모두 적용PERSIST
: 영속REMOVE
: 삭제MERGE
: 병합REFRESH
: refreshDETACH
: detch: 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제됨
📌 주의할 점
- 참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 보고 삭제하는 기능이다.
- 참조하는 곳이 하나일 때 사용해야 한다.
- 특정 엔티티가 개인 소유할 때 사용한다.
@OneToOne
,@OneToMany
만 가능개념적으로 부모를 제거하면 자식은 고아가 된다. 따라서 고아 객체 제거 기능을 활성화하면 부모를 제거할 때 자식도 함께 제거된다.
➡️ CascadeType.REMOVE처럼 동작
: CascadeType.ALL + orphanRemovel=true
em.persist()
로 영속화, em.remove()
로 제거실전예제는
jpabook/jpashop
프로젝트로 진행됩니다.
DB:/~/jpashop
@ManyToOne
, @OneToOne
은 기본이 즉시 로딩이므로 지연 로딩으로 변경