<aside> ❗

[두 가지 경우]
<aside> ❗
[em.find() VS em.getReference()]
em.find() : 데이터베이스를 통해서 실제 엔티티 객체 조회em.getReference() : 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회
→ DB에 쿼리가 안 나갔는데 객체가 조회가 되는 것
프록시 클래스는 구조는 동일하지만 값이 모두 비어있다고 보면 됨target은 진짜 참조를 가리킨다. // 처음에는 null로 비어있음프록시 클래스는 실제 엔티티를 상속 받아서 만들어진다. // 하이버네이트가 내부적으로 만들어 냄Member member = new Member();
member.setName("hello");
em.persist(member);
em.flush();
em.clear();
//프록시 객체를 가져옴
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("findMember = " + findMember.getClass());
// 값이 실제 사용되는 시점에 쿼리가 나간다.
// 찾을 때, ID값을 알고 있으니 조회하지 않음
System.out.println("findMember.getId() = " + findMember.getId());
// Name의 값은 알 수 없으므로 영속성 컨텍스트에 실제 객체를 요청
System.out.println("findMember.getName() = " + findMember.getName());
System.out.println("findMember.getName() = " + findMember.getName());
System.out.println("findMember = " + findMember.getClass());
/*
// 하이버네이트가 강제로 만든 프록시 클래스
findMember = class jpabook.study.domain.Member$HibernateProxy$3UMPnBJl
*/
</aside>
<aside> ❗

진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨(이론상)프록시 객체는 **실제 객체의 참조(target)**를 보관프록시 객체를 호출하면 프록시 객체는 실제 객체의 메서드 호출
// member에 프록시 객체를 저장함
Member member = em.getReference(Member.class, "id1");
member.getName();

JPA가 영속성 컨텍스트에 요청 ( 진짜 Member 객체를 가져오도록 )영속성 컨텍스트는 DB를 조회해서 진짜 Member 엔티티를 생성target이 진짜 엔티티를 참조하도록 함target.getName() 수행초기화 - DB를 통해서 데이터를 가져와 엔티티를 만드는 과정
</aside><aside> ❗
한 번만 초기화초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님
초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능한 것
즉, 프록시 클래스의 target에 참조값이 들어가는 것따라서 타입 체크시 주의해야함
** **(== 비교 실패, 대신 instance of 사용해야 함) ****Member m1 = em.find(Member.class, member1.getId());
Member m2 = em.getReference(Member.class, member2.getId());
// 절대 타입 비교를 == 을 사용해서는 안 된다.
System.out.println(m1.getClass() == m2.getClass()); // false
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도
실제 엔티티 반환
즉, 영속성 컨테이너에서 해당 엔티티를 관리하는 시점에 em.getReference()를 호출해도 실제 엔티티를 반환함
→ 이미 영속성 컨텍스트에 있기 때문
→ 영속성 컨텍스트에 프록시 클래스가 등록되면 이후 find()로 가져와도
1차 캐시에 있는 프록시 클래스를 반환한다. (반대도 마찬가지)
같은 영속성 컨텍스트에서 같은 엔티티 ID를 가지는 객체는 항상 같은 인스턴스를 반환하도록 보장함Member m1 = em.find(Member.class, member1.getId());
Member m2 = em.getReference(Member.class, member2.getId());
// 영속성 캐시에서 가져오기 때문에 프록시가 m2에 저장됨
m2 = em.find(Member.class, member2.getId());
System.out.println(m2.getClass().getName());
System.out.println(m1.getClass() == m2.getClass());
/*
jpabook.study.domain.Member$HibernateProxy$SWAvcM1M
false
*/
준영속 상태일 때, 프록시를 초기화하면 문제 발생
(하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)Member member = em.getReference(Member.class, member.getId());
// member는 비영속 상태가 됨 -> 영속성 컨텍스트 도움을 받지 못함
em.detach(member);
member.getName()
/*
org.hibernate.LazyInitializationException 예외를 터트림
could not initialize proxy 오류 발생
no Session
*/
</aside>
<aside> ❗
PersistenceUnitUtil.isLoaded(Object entity)emf.getPersistenceUnitUtil().isLoaded(member);
// false -> 초기화 X
// true -> 초기화 O
Hibernate.initialize(entity)
[참고]
<aside> ❗

지연 로딩 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; // team을 프록시 타입으로 조회함, 즉 Member만 DB에서 조회
}
</aside>
<aside> ❗


초기화를 통해 실제 엔티티를 가져와 프록시가 참조하도록 한다.Team team = member.getTeam();
team.getName(); // 실제 team을 사용하는 시점에 초기화(DB 조회)
</aside>
<aside> ❗
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
}
</aside>
<aside> ❗


조인을 사용해서 SQL 한번에 함께 조회<aside> ❗
</aside>