Search
Duplicate

[Spring JPA] 프록시

날짜
2022/04/29 05:22
상태
스프링 JPA 기본
속성 1
Spring
JAVA
JPA
담당자

Member를 조회할 때 Team도 함께 조회해야 할까?

문제 발생
우리가 연관관계를 맺음으로써 회원의 이름만 조회하고 싶은데, 팀의 정보도 맵핑이 돼서 같이 조회되는 자원낭비가 발생할 수 있다. 이를 해결하기 위해 프록시를 사용한다.
회원과 팀 함께 출력
public void printUserAndTeam(String memberId) { Member member = em.find(Member.class, memberId); Team team = member.getTeam(); System.out.println("회원 이름: " + member.getUsername()); System.out.println("소속팀: " + team.getName()); }
Java
복사
회원만 출력
public void printUser(String memberId) { Member member = em.find(Member.class, memberId); Team team = member.getTeam(); System.out.println("회원 이름: " + member.getUsername()); }
Java
복사

프록시 기초

em.find() vs em.getReference()
em.find(): 데이터베이스를 통해 실제 엔티티 객체 조회
em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회

프록시 특징

실제 클래스를 상속받아서 만들어짐
실제 클래스와 겉모양이 같다.
사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨.

프록시 특징

프록시 객체는 실제 객체의 참조(target)를 보관
프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출

프록시 객체의 초기화

Member member = em.getReference(Member.class, “id1”); member.getName();
Java
복사
1.
em.getReference를 호출하게 되면 Entity traget이 null 값으로 멤버 프록시 객체를 가져오게 된다.
2.
member.getName()을 호출하면 멤버 타깃의 값이 없다. 없으므로 초기화를 요청한다.
3.
JPA가 영속성 컨텍스트에 초기화 요청을 한다.
4.
영속성 컨텍스트가 DB를 조회한 후 실제 Entity를 Member객체에 전달해준다.
5.
이후 MemberPorxy 객체의 멤버 타깃에 멤버의 참조값을 넣어준다.
package helloJpa; ... public class JpaMain { public static void main(String[] args) { ... try { Member member = new Member(); member.setName("hello "); em.persist(member); em.flush(); em.clear(); System.out.println("============call getReference START =========="); Member findMember = em.getReference(Member.class, member.getId()); System.out.println("============call getReference END =========="); System.out.println("============call findMember.getName() START =========="); System.out.println("findMember.userName = " + findMember.getName()); System.out.println("============call findMember.getName() END =========="); System.out.println("findMember.userName = " + findMember.getName()); tx.commit(); } catch (Exception e) { tx.rollback(); } finally { em.close(); } emf.close(); } }
Java
복사
위 예제를 보면 조회되는 순간이 findMember.getName(); 일 때 한번 작동하는 것을 확인할 수 있다.

프록시의 특징

프록시 객체는 처음 사용할 때 한 번만 초기화
프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초기화되면서 프록시 객체를 통해서 실제 엔티티에 접근 가능
프록시 객체는 원본 엔티티를 상속 받음, 따라서 타입 체크 시 주의해야 함 (== 비교 실패, 대신 instance of 사용)
영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환
영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생(하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)
프록시 확인
프록시 인스턴스의 초기화 여부 확인
PersistenceUnitUtil.isLoaded(Object entity)
프록시 클래스 확인 방법
entity.getClass(). getName() 출력
프록시 강제 초기화
Hiebernate.initialize(Entity);
참고 : JPA 표준은 강제 초기화 없음
강제 호출 : member.getName();
이 글은 인프런의
제목 : 자바 ORM 표준 JPA 프로그래밍 - 기본 편
강사 : 김영한 님의 동영상을 참조해 만들었습니다.