프록시
- 엔티티를 조회할 때 연관된 엔티티들이 항상 사용되는 것은 아니다.
- JPA는 이런 문제를 해결하려고 엔티티가 실제 사용될 때까지 데이터베이스 조회를 지연하는 방법을 제공하는데 이를 지연 로딩이라고 한다.
- 지연 로딩 기능을 사용하려면 실제 엔티티 객체 대신에 데이터베이스를 지연할 수 있는 가짜 객체가 필요한데 이를 프록시 객체라고 한다.
프록시 동작
- 엔티티를 실제 사용하는 시점까지 데이터베이스 조회를 미루고 싶으면 em.getReference() 메서드를 사용하면 된다.
- 이 메소드를 호출할 때 JPA는 데이터베이스를 조회하지 않고 실제 엔티티 객체도 생성하지 않는다.
- 대신 데이터베이스에 접근을 위임한 프록시 객체를 반환한다.
- 프록시 객체에 member.getName()을 호출해서 실제 데이터를 조회
- 프록시 객체는 실제 엔티티가 생성되어 있지 않으면 영속성 컨텍스트에 실제 엔티티 생성을 요청하는데 이를 초기화라 한다.
- 영속성 컨텍스트는 데이터베이스를 조회해서 실제 엔티티 객체를 생성
- 프록시 객체는 생성된 실제 엔티티 객체의 참조를 Member target 맴버변수에 보관
- 프록시 객체는 실제 엔티티 객체의 getName() 을 호출해서 결과를 반환
프록시 특징
- 프록시 객체는 처음 사용할 때 한번만 초기화된다.
- 프록시 객체를 초기화한다고 프록시 객체가 실제 엔티티로 바뀌는 것은 아니다.
- 프록시 객체가 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근할 수 있다.
- 프록시 객체는 원본 엔티티를 상속받은 객체이므로 타입 체크 시 주의해야함.
- 영속성 컨텍스트에 찾는 엔티티가 존재하면 getTeference()를 호출해도 실제 엔티티를 반환
- 초기화는 영속성 컨텍스트의 도움을 받아야 가능한데 도움을 받을 수 없는 준영속 상태의 프록시를 초기화시 LazyInitializationException 예외 발생
- 예시 : em.close() 메소드로 영속성 컨트스트를 종료하고 프록시를 초기화하면 예외가 발생
프록시와 식별자
- 엔티티를 프록시로 조회할 때 식별자(PK) 값을 파라미터로 전달하는데 프록시 객체는 이 식별자 값을 보관한다.
1
2
Team team=em.getReference(Team.class,"team"); //식별자 보관
team.getId(); //초기화되지 않음
- 위의 경우는 @Access(AccessType.PROPERTY)에만 적용된다.
- 만약 AccessType.FIELD로 한다면 getId()메소드가 id만 조회하는 메소드인지 알지 못하므로 프록시를 초기화한다.
프록시 확인
- JPA가 제공하는
PersistenceUnitUtil.isLoaded(Object entity)
메소드를 사용하면 프록시 인스턴스의 초기화 여부를 확인할 수 있다.
프록시와 즉시 로딩, 지연 로딩
- 처음부터 연관된 엔티티를 모두 영속성 컨특스트에 올려두는 것은 현실적이지 않고, 필요할 때마다 SQL을 실행해서 연관된 엔티티를 지연 로딩하는 것도 최적화 관점에서 보면 꼭 좋지많은 않다.
- 항상 같이 쓴다면 즉시 로딩으로 SQL 조인을 사용해서 조회하는 것이 더 효율적이다.