지연로딩을 이해 하기 위해서는 프록시에 대해 이해해야 쉽게 다가갈 수 있다.
강의에서는 시작을 해당 질문으로 시작한다.
" Member 엔티티를 조회할 때 Team 도 함께 조회해야 할까 ? "
해당 질문에 대한 답은 정해져있지 않다.
실제로 비즈니스 로직이 Member 엔티티를 조회할 때 꼭 Team 을 필요로 해야할 수도 있기 때문이다.
하지만, 보통은 같이 사용하지 않는 것이 일반적이다.
- 비즈니스 로직에 꼭 필요하지 않다면 Team을 같이 조회할 필요가 없다.
- 쿼리 등 낭비가 발생한다.
- 해당 낭비를 줄이기 위해, 지연로딩과 프록시로 해결한다.
JPA 에서는 em.find () 뿐만 아니라, em.getReference() 메서드도 제공하고 있다.
em.find는 DB 를 통해서 실제 엔티티 객체를 조회하는 메서드이고
em.getRefence는 가짜 프록시 엔티티 객체를 조회하는 메서드 이다.
Member 엔티티를 조회할때 team 이 같이 조회되는 예시를 보자
em.find() 사용
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
@Column(name = "username")
private String name;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
Member member = new Member();
member.setName("member1");
em.persist(member);
em.flush();
em.clear();
Member findMember = em.find(Member.class, member.getId());
tx.commit();
쿼리를 보면 team 까지 같이 select 된 것을 볼 수 있다.
Hibernate:
select
member0_.member_id as member_i1_6_0_,
member0_.city as city2_6_0_,
member0_.street as street3_6_0_,
member0_.zipcode as zipcode4_6_0_,
member0_.username as username5_6_0_,
member0_.team_id as team_id6_6_0_,
team1_.team_id as team_id1_11_1_,
team1_.insert_member as insert_m2_11_1_,
team1_.createdDate as createdD3_11_1_,
team1_.update_member as update_m4_11_1_,
team1_.modifiedDate as modified5_11_1_,
team1_.name as name6_11_1_
from
Member member0_
left outer join
Team team1_
on member0_.team_id=team1_.team_id
where
member0_.member_id=?
em.getReference( ) 메서드를 사용해서 Class형태를 보면 Proxy 형태로 나오는 것을 볼 수 있다.
현재 프록시 객체를 얻기는 했지만, 아무런 동작을 하지 않았기 때문에 select 쿼리를 수행하지 않는다.
Member member = new Member();
member.setName("proxy");
em.persist(member);
em.flush();
em.clear();
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("findMember.getClass() = " + findMember.getClass());
tx.commit();
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(city, street, zipcode, username, team_id, member_id)
values
(?, ?, ?, ?, ?, ?)
findMember.getClass() = class hellojpa.Member$HibernateProxy$oN6J2xMt
프록시의 특징
- 실제 클래스를 상속받아서 만들어진다.
- 실제 클래스와 겉 모양이 같다.
프록시 객체의 초기화는 프록시 객체의 메서드를 호출할때 초기화가 된다. ( 처음 사용할 때 )
프록시 객체를 초기화하면 실제 엔티티로 바뀌는것아 아니다.
프록시 객체는 원본 엔티티를 상속받으므로, instanceof 를 통해 비교해야 한다 ( == 비교불가 )
getName 메서드를 통해 초기화 요청을 한다. ( 프록시객체에는 target 값이 존재하지 않는다 )
영속성 컨텍스트가 DB를 조회해서 실제 Entity 값을 생성해준다
프록시 객체는 실제 엔티티의 getName()을 호출해서 원하는 getName 값을 받을 수 있다.
이후에는 해당 과정없이 사용이 가능하다
참고
https://ict-nroo.tistory.com/131
https://victorydntmd.tistory.com/210
https://www.inflearn.com/course/ORM-JPA-Basic
'Spring > JPA' 카테고리의 다른 글
JPQL - Query타입, 결과조회, 파라미터, 프로젝션, 페이징, 조인 (0) | 2022.09.27 |
---|---|
즉시로딩 지연로딩 (0) | 2022.09.27 |
상속관계 매핑 ( @Inheritance , @DiscriminatorColumn ) (0) | 2022.09.23 |
연관 관계 매핑 (1) | 2022.09.22 |
연관 관계가 필요한 이유 (0) | 2022.09.21 |
댓글