[인프런 김영한] JPA - 프록시

2021. 2. 10. 16:21프로그래밍 언어/Spring Framework

[인프런 김영한] JPA - 프록시


해당 글은 인프런 김영한강사님의 영상을 보고 정리한 글입니다.

Spring Boot, Spring Data JPA를 사용해 실습하였습니다.

김영한 인프런 : www.inflearn.com/users/@yh

 

인프런 - 김영한의 강의들을 만나보세요.

우아한형제들 개발 팀장 (전: 카카오, SK플래닛) 저서: 자바 ORM 표준 JPA 프로그래밍

www.inflearn.com


▣ 프록시

 - em.find() : 데이터베이스를 통해서 실제 Entity 객체 조회

 - em.getReference() : 데이터베이스 조회를 미루는 가짜(프록시) Entitty 조회

 

* 실제 클래스를 상속 받아서 만들어짐

 - 실제 클래스와 겉 모양은 같음

 - 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨 (이론상)

* 프록시 객체는 실제 객체의 참조(target)를 보관

 - 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출

 


▣ find() vs getOne()

 

 

Member

@Entity
public class Member{

    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

 

 

Team

@Entity
public class Team{

    @Id
    @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String name;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    List<Member> members = new ArrayList<>();
}

 

 

예제코드 - 실행

Member member = new Member();
member.setName("user1");
memberRepository.save(member);

Member member1 = memberRepository.findById(member.getId()).get();
System.out.println(findMember.getId());
System.out.println(findMember.getName());


// find() 메소드를 사용하면 reference를 호출하는 시점에 select 쿼리가 출력이 된다.
Member findMember = memberRepository.findById(member.getId()).get();
// --여기까지 실행하면 select 쿼리가 실행된다. --
System.out.println(findMember.getId());
System.out.println(findMember.getName());
// --여기까지 실행하면 select 쿼리가 실행된다. --


// EntityManager의 getReference = JpaRepositocy의 getOne과 동일하다.

Member findMember2 = memberRepository.getOne(member.getId());

// --여기까지 실행하면 select 쿼리가 실행이 안된다. --
// System.out.println(findMember.getId());
// System.out.println(findMember.getName());
// --여기까지 실행하면 select 쿼리가 실행된다. --


// select 쿼리
Hibernate: 
    select
        member0_.member_id as member_i1_0_0_,
        member0_.name as name2_0_0_,
        member0_.team_id as team_id3_0_0_,
        team1_.team_id as team_id1_1_1_,
        team1_.name as name2_1_1_ 
    from
        member member0_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id 
    where
        member0_.member_id=?

| find 메소드를 사용하면 해당 순간 select 쿼리가 실행이 되는데

getOne을 사용하게 되면 해당 값이 실제로 사용되는 시점에 select 쿼리가 실행된다.

 

 

 

 

▣ getOne이 가지고온 Class는 뭘까?

System.out.println(findMember2.getClass());

// 출력
class com.spring.jpa.Member$HibernateProxy$HmOVyLMj

| Hibernate가 만든 가짜 Proxy.

 

 

 

 

▣ 프록시의 특징 **

* 프록시 객체는 처음 사용할 때 한번만 초기화

* 프록시 객체를 초기화 할 때, 프록시 객체가 실제 Entity로 바뀌는게 아닌, 프록시 객체를 통해서 실제 Entity에 접근

* 프록시 객체는 원본 Entity를 상속받으며 타입 체크시 주의해야함. (== 대신, instance of 사용하기)

* 영속성 컨텍스트에 찾는 Entity가 이미 있으면 getReference()를 호출해도 실제 Entity를 반환.

* 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생.

 - 영속성 컨텍스트가 close(), clear() 되면 Exception 발생.