Computer Science/Spring & Java

[JPA]스프링 프록시

콩순이냉장고 2022. 11. 21. 01:23

프록시 

 

엔티티를 조회할 때 연관된 엔티티들이 항상 사용되는 것은 아니다. 예를 들자면

@Entity
 public class Member { 
 @Id @GeneratedValue
 private Long id;
 @Column(name = "USERNAME")
 private String name;
 private int age;
 @ManyToOne
 @JoinColumn(name = "TEAM_ID")
 private Team team;
 }
 
 @Entity
 public class Team {
 @Id @GeneratedValue
 private Long id;
 private String name;
 … 
 }

와같이 Member 와 Team 의 N:1 관계에서 

Member의 데이터만 출력하고 싶은데 

public void printUser(String memberId) {
 Member member = em.find(Member.class, memberId);
 Team team = member.getTeam();
 System.out.println("회원 이름: " + member.getUsername()); 
}

printUser 메소드는 회원 엔티티만 출력하는데 사용하고 회원과 연관된 팀 엔티티는 전혀 사용되지 않지만

em.find()로 회원 엔티티를 조회할 때 회원과 연관된 팀 엔티티(Member.team)까지 데이터베이스에서 함께 조회 하기 시작하기 때문에 효율적이지 않다

 

JPA는 이런 문제를해결하기위해 엔티티가 실제 사용될 때까지 데이터베이스 조회를 지연하는 방법을 제공하는데

이런방법을 지연로딩이라 한다.

 

실제 사용하는 시점까지 데이터베이스에서 조회를 미루고 싶으면

EntityManage.getReference()를 사용

 

프록시 객체 : 데이터베이스 조회를 지연할 수 있는 가짜 객체

 

프록시 특징  

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

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

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

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

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

 

프록시 객체는 member.getName() 처럼 실제 사용될 때 데이터베이스를 조회해서 실제 엔티티 객체를 생성함

이것을 프록시 객체의 초기화라 함

 

프록시 객체의 초기화

 

Member member = em.getReference(Member.class, “id1”); 
member.getName();

프록시의 특징

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

• 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초 기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능

• 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함 (== 비 교 실패, 대신 instance of 사용) • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해 도 실제 엔티티 반환

• 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생 (하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)

 

프록시 확인방법

• 프록시 인스턴스의 초기화 여부 확인 PersistenceUnitUtil.isLoaded(Object entity)

• 프록시 클래스 확인 방법 entity.getClass().getName() 출력(..javasist.. or HibernateProxy…)

• 프록시 강제 초기화 org.hibernate.Hibernate.initialize(entity)