Programming/JPA(KYH)

JPA - 프록시

잇(IT) 2023. 7. 17. 09:55

- 프록시 기초

 

- em.find() vs em.getReference()

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

2. em.getReference() : 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회


- 프록시 특징

1. 실제 클래스를 상속 받아서 만들어진다.

2. 실제 클래스와 겉 모양이 같다.

3. 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.

4. 프록시 객체는 실제 객체의 참조(target)를 보관한다.

5. 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.


- 프록시 객체의 초기화 

Member findMember = em.getReference(Member.class, member.getId());
findMember.getName();

1. em.getReference가 호출되면 프록시 객체가 생성된다. 현재 프록시 객체의 target에는 데이터가 없다.

2. getName() 메서드가 호출되면 프록시 객체는 영속성 컨텍스트에 초기화를 요청한다.

3. 영속성 컨텍스트는 프록시로부터 초기화 요청을 받으면 DB에서 실제 객체의 데이터를 가져오고 실제 Entity를 생성한다. (이때 쿼리가 나간다.)

4. 실제 Entity가 생성되면 프록시 객체의 target에 실제 Entity가 연결되서 객체에 대한 데이터를 가져올 수 있다.


- 특징

1. 프록시 객체는 처음 사용할 때 한 번만 초기화
2.프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능하다.
3. 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함 (== 비교 실패, 대신 instance of 사용)
4. 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환
5. 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면문제 발생
(하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)

 

- find -> getReference

Member member = new Member();
member.setUsername("hello");
em.persist(member);
            
em.flush();
em.clear();'

Member member1 = em.find(Member.class, member.getId());
System.out.println("member1.getClass() = " + member1.getClass());
Member reference = em.getReference(Member.class, member.getId());
System.out.println("reference = " + reference.getClass());

- find에서 가져온 실제 Member 객체를 둘 다 사용한다. 이미 영속성 컨텍스트에 실제 객체가 있는데 굳이 프록시 객체를 생성해서 또 가져올 필요가 없기 때문이다.

- 또 JPA는 동일 트랜잭션 내에서 같은 객체에 대해 ==을 만족시켜야 하기 때문이다.

 

- getReference -> find

Member member = new Member();
member.setUsername("hello");
em.persist(member);
            
em.flush();
em.clear();'

Member member1 = em.find(Member.class, member.getId());
System.out.println("member1.getClass() = " + member1.getClass());
Member reference = em.getReference(Member.class, member.getId());
System.out.println("reference = " + reference.getClass());

- 동일 트랜잭션 내에서 JPA는 동일 객체에 대해 ==을 만족시켜야 하기 때문에 getReference로 프록시 객체가 호출되면 find() 메서드 실행 시 프록시 객체를 사용한다.

- 프록시 객체도 결국 실제 엔티티를 상속 받아서 사용하는 것이기 때문에 가능한 것이다.

- 추가로 getReference() 메서드를 호출하게 되면 select 쿼리가 바로 DB에 조회되는 것이 아닌 데이터를 필요로 하게 되면 쿼리를 날리는 것을 확인 할 수 있다.


- 준영속 상태의 프록시 호출

Member reference = em.getReference(Member.class, member.getId());
System.out.println("reference.getClass() = " + reference.getClass());
            
em.detach(reference);

reference.getUsername();
System.out.println("reference.getUsername() = " + reference.getUsername());

- 프록시 객체는 영속 컨텍스트에서 실제 데이터를 가져오기 때문에 프록시 객체 호출 뒤 준영속 상태로 전환하게 되면 getUsername() 메서드와 같이 데이터를 가져와야 하는 작업이 실행되게 되면 예외가 발생하는 것을 확인할 수 있다.


- 인스턴스의 초기화 여부 확인

- 강제 초기화

 

 

 

 

 

 

 

 

 

 

 

 

출처 : 인프런 - 우아한 형제들 기술이사 김영한의 스프링 완전 정복 (자바 ORM 표준 JPA 프로그래밍 - 기본편)

728x90

'Programming > JPA(KYH)' 카테고리의 다른 글

JPA - 영속성 전이(CASCADE)와 고아 객체  (0) 2023.07.17
JPA - 즉시 로딩, 지연 로딩  (0) 2023.07.17
JPA - 고급 매핑  (0) 2023.07.14
JPA - 다양한 연관관계 매핑  (0) 2023.07.14
JPA - 연관관계 매핑 기초  (0) 2023.07.14