
🤔
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 조인이나, 엔티티 그래프 기능을 사용해라!- 즉시 로딩은 상상하지 못한 쿼리가 나간다.