✅ 프록시 객체 : 지연로딩
즉 엔티티의 연관된 데이터를 필요할 때만 로드하기 위해 사용
find()
: 데이터베이스를 통해서 실제 엔티티 객체를 조회
getReference()
: 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체를 조회
✅ 프록시 객체의 초기화
target에 값이 없음 -> jpa가 영속성 컨텍스트에 요청 -> 영속성 컨텍스트가 DB를 조회함
-> 실제 엔티티를 생성해서 줌 -> target이 생성한 엔티티를 연결함
-> 한번만 초기화하면 다시 DB를 조회할 필요 없음
✅ 프록시 객체의 특징
Member findMember = em.getReference(Member.class, member.getId());
System.out.println(findMember.getClass()); // Member 클래스가 아닌 프록시 클래스로 나옴
instance of
사용Member m1 = em.find(Member.class, member.getId());
Member m2 = em.getReference(Member.class, member.getId());
System.out.println(m1.getClass() == m2.getClass()); // 타입이 다르기때문에 false 나옴
System.out.println(m1 instance of Member); // true
System.out.println(m2 instance of Member); // true
영속성 컨텍스트에 찾는 엔티티가 이미 있으면, em.getReference()
를 호출해도 실제 엔티티 반환
영속성 컨텍스트의 도움을 받을 수 없는 준영속
상태일 때, 프록시 초기화시 문제발생
-> 하이버네이트는 org.hibernate.LazyInitalizationException
예외를 터뜨림
Member member1 = new Member();
member1.setUsername("최최최");
em.persist(member1); // 영속성컨텍스트에 영속상태로 저장
em.flush(); // 영속성 컨테스트의 변경내용을 데이터베이스에 반영 (쿼리던짐)
em.clear();
Member refMember = em.getReference(Member.class, member1.getId());
em.clear(); // 준영속상태
refMember.getUsername(); // 오류발생
getReference
: 진짜 객체를 주는게 아니라, 가짜(프록시) 엔티티 객체를 조회
✅ 지연로딩 : 조인된 클래스를 자주 조회하지 않을 경우 사용
public class Member{
@Id
@GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne(fetch = FetchType.LAZY) // fetch = FetchType.LAZY 지연로딩 사용
@JoinColumn(name = "TEAM_ID")
private Team team;
}
team을 지연로딩으로 사용했을 때, member을 찾았을 경우 Team이 조회되지 않는다
직접team.getName()
과 같이 메소드를 호출 했을 때 DB 쿼리가 날라간다
이런식으로 지연로딩을 하면 member 만 사용하고싶을 때 굳이 team까지 찾는 쿼리를 날릴 필요가 없다
즉 쉽게말해서 지연로딩을 하면 조인된 클래스를 본인이 원하는 시점에서 조회할 수 있음
✅ 즉시로딩
위와 같이 반대로 Member
클래스를 사용할 때 Team
클래스도 자주 같이 사용하는 경우
fetch = FetchType.EAGER
을 사용한다
그러면 Member member = em.find(Member.class, 1L);
를 입력했을 때
Member를 찾을때부터 Team도 한번에 조인해서 조회할 수 있다
✅ 실무 팁
지연로딩
만 사용하자 = 모든 연관관계는 지연로딩
을 사용fetch 조인
+ 엔티티 그래프
기능을 사용@ManyToOne
OneToOne
-> 기본이 즉시 로딩으로 LAZY
로 설정해야함@OneToMany
ManyToMany
-> 기본이 지연로딩임✅ 영속성 전이 CASCADE
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
✅ 고아 객체 제거
CascadeType.REMOVE
처럼 동작함 orphanRemoval = true
Parent parent1 = em.find(Parent.class, id);
parent1.getChildren().remove(0); // 자식 엔티티를 컬렉션에서 제거
-> 그후에 delete 쿼리가 나감