
해당 시리즈는 김영한님의 JPA 로드맵을 따라 학습하면서 내용을 정리하는 글입니다
em.find() vs em.getReference()em.find(): 데이터 베이스를 통해서 실제 엔티티 객체 조회em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 조회getReference하는 시점에는 쿼리를 날리지 않다가 실제로 사용하는 시점에서 쿼리를 날려서 데이터베이스에서 가져옵니다target)를 보관합니다Member member = em.getReference(Member.class, "id1");
member.getName();

getName이 호출되는 시점에 영속성 컨텍스트에 요청을 합니다target에 실제 객체를 매핑해 줍니다em.getReference()를 호출해도 실제 엔티티 반환JPA에서는 같은 트랜잭션에서 타입이 같은 두 객체를 비교할 때(==) 무조건 true를 보장하기 때문에 이미 영속성 컨텍스트에 있으면 프록시 객체를 생성하지 않고 실제 엔티티를 반환합니다하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트립니다)PersistenceUnitUtil.isLoaded(Object entity)EntityManagerFactory emf = Persistence.createEntityManagerFactory(...);
emf.getPersistenceUnitUtil().isLoaded(entity);
entity.getClass().getName() 출력org.hibernate.Hibernate.initialize(entity);JPA 표준은 강제 초기화가 없습니다member.getName() 등으로 초기화 유도System.out.println(member.getName());@Entity
public class Member {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}

Team 객체는 프록시 객체로 조회하게 됩니다Team 객체를 사용하는 시점에 쿼리를 조회해서 진짜 Team의 엔티티로 교체됩니다@Entity
public class Member {
...
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}

Member 객체를 조회해서 사용할 때 많은 경우 Team 객체를 같이 사용하는 로직이 대부분이라면 EAGER 옵션을 통해서 즉시로딩을 하는게 좋습니다(두 번의 쿼리가 안나갈 수 있기 때문입니다)SQL이 발생합니다JOIN이 발생할 수 있습니다JPQL에서 N + 1 문제를 일으킵니다 List<Member> members = em.createQuery("select m from Member m", Member.class)
.getResultList();JPQL은 SQL로 번역되고 실행됩니다. 따라서 위 코드도 SQL로 번역되고 쿼리문 대로 Member만 선택하게 됩니다. 하지만 설정에 EAGER로 되어있으면 즉시로딩이기 때문에 한번 더 쿼리를 보내서 Member마다 연결된 Team의 정보까지 불러오는 겁니다. 원치않게 쿼리가 두번 실행되게 됩니다@ManyToOne, @OneToOne은 기본이 즉시 로딩입니다 -> LAZY로 설정해야 합니다@OneToMany, @ManyToMany는 기본이 지연 로딩입니다JPQL fetch 조인이나, 엔티티 그래프 기능을 사용하도록 합니다orphanRemoval = trueParent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0);
// 자식 엔티티를 컬렉션에서 제거
DELETE FROM CHILD WHERE ID=?@OneToOne, @OneToMany만 가능CascadeType.REMOVE 처럼 동작합니다em.persist()로 영속화하고, em.remove()를 통해서 제거합니다Aggregate Root 개념을 구현할 때 유용합니다