🤔
Member
를 조회할 때 항상Team
도 함께 조회해야 할까?
비즈니스 상황에 따라서 다르겠지만,
printUserAndTeam
은 Member
, Team
을 따로 가져오는 것보다 한번에 같이 가져오는 것이 성능상 이득이다.printUser
의 경우 Team
데이터까지 같이 가져올 필요가 없다. (낭비이기 때문!)JPA는 이러한 낭비를 하지 않기 위해
프록시
와지연 로딩
으로 해결한다!
em.find()
: 데이터베이스를 통해서 실제 엔티티 객체 조회em.getReference()
: 데이터베이스 조회를 미루는 가짜 (프록시) 엔티티 객체 조회프록시 클래스는 실제 클래스를 상속 받아서 만들어지기 때문에 실제 클래스와 겉 모양이 같다.
우리는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.
target
)를 보관한다.Member member = em.getReference(Member.class, “id1”);
member.getName();
getReference()
를 호출해서 프록시 객체를 가져온 다음, getName()
을 호출한다.getName()
이 호출되면 JPA
가 영속성 컨텍스트에 초기화 요청을 한다.Member
프록시의 Member target
에 실제 Entity 객체를 연결한다.target.getName()
이 호출되면서 원하는 로직을 실행한다.📌 프록시 특징 정리
- 프록시 객체는 처음 사용할 때 한 번만 초기화된다.
- 프록시 객체를 초기화한다고 프록시 객체가 실제 엔티티로 바뀌는 것은 ❌! → 프록시 객체를 통해서 실제 엔티티에 접근할 수 있게 되는 것!
- 프록시 객체는 원본 엔티티를 상속 받은 객체이므로, 타입 체크시 주의해야 한다!
==
비교 실패, 대신instance of
사용- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면
em.getReference()
를 호출해도 실제 엔티티를 반환한다.- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생한다!
- 하이버네이트는
org.hibernate.LazyInitializationException
예외를 터트린다.
PersistenceUnitUtil.isLoaded(Object entity)
false
반환entity.getClass().getName()
org.hibernate.Hibernate.initialize(entity);
📌 참고
- JPA 표준은 강제 초기화 없음
- 강제 호출:
member.getName()
LAZY
를 사용해서 프록시로 조회@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
@ManyToOne(fetch = FetchType.LAZY)
Team team
객체는 프록시 객체로 조회한다.member1
로딩 시 연관된 Team
엔티티는 DB에서 조회해오지 않고, 프록시로 초기화해준다.Member
조회 시 Team
객체는 프록시 객체로 초기화된다.team
의 데이터를 참조하는 시점에 프록시 객체 team
은 실제 엔티티를 가르키도록 초기화한다.
Member
와Team
을 자주 함께 사용한다면 함께 조회하는 것이 좋다.
EAGER
를 사용해서 함께 조회@Entity
public class Member {
...
@ManyToOne(fetch = FetchType.EAGER
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
Member
객체를 조회할 때, Team
객체 정보까지 한번에 조회한다.Team
객체는 프록시가 아닌 실제 엔티티 객체이다!member1
로딩 시 team1
까지 조인해서 조회한다.가급적 지연 로딩만 사용해야 한다! (특히 실무에서)
find()
한번 수행하면 다수의 테이블이 조인된다 → 성능 저하😣@ManyToOne
, @OneToOne
은 기본이 즉시 로딩 (EAGER
)이므로 LAZY
로 설정해야 한다.@OneToMany
, @ManyToMany
는 기본이 지연 로딩 (LAZY
)이다.✋🏻 잠깐!!
이론적인 활용이기 때문에 실무에서 이런식으로 하면 큰일난다! (실무에서는 무조건 지연 로딩만!)
Member
와 Team
은 자주 함께 사용 → 즉시 로딩Member
와 Order
는 가끔 사용 → 지연 로딩Order
와 Product
는 자주 함께 사용 → 즉시 로딩member1
조회 시,
team
은 조인하고orders
는 (LAZY
가 걸려있으므로) 프록시 객체로 초기화한다.orders
의 데이터를 참조하는 시점에 실제 엔티티로 가리키도록 초기화된다.⭐ 실무에서
- 모든 연관관계에 지연 로딩을 사용해라!
- 실무에서 즉시 로딩을 사용하지 마라!
JPQL fetch 조인
이나, 엔티티 그래프 기능을 사용해라!- 즉시 로딩은 상상하지 못한 쿼리가 나간다.