728x90
반응형
프록시 객체란?
- 해당 엔티티와 연관된 다른 엔티티를 한 번에 조회하지 않고 실제로 필요할 때 프록시 객체를 초기화시켜 사용한다.
- 프록시 객체를 사용하면 자원 낭비를 막을 수 있다.
- 지연 로딩을 이해하기 위해 필요한 기초 개념
- em.getReference()라는 메서드로 프록시 객체인지 확인 가능
- em.find()는 실제 엔티티 객체를 조회하는 메서드
프록시의 특징
- 실제 클래스를 상속받아서 만들어진다.
- 하이버네이트가 내부적으로 상속받음
- 실제 클래스와 동일한 타입처럼 보인다.
- 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용한다.
- 프록시 객체는 실제 객체의 참조대상을 보관한다.
- 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.
프록시 객체의 초기화
- Member객체를 em.getReference(Member.class,member.getId())로 호출한다.
- MemberProxy객체에 처음에는 target값이 존재하지 않는다. 아직 실제 Member를 불러오지 않았기 때문
- 영속성 컨텍스트에 초기화 요청을 한다
- 컨텍스트가 DB에서 실제 Member객체가 존재하면 불러와 Member객체를 생성해준다.
- 프록시 객체가 갖고 있는 target의 getName을 target에서 가지고온다
- 프록시 객체에 target이 참조되면, 더 이상 프록시 객체의 초기화 동작은 없어도 된다.
프록시 개념 정리
- 프록시 객체는 처음 사용할 떄 한 번 초기화 된다
- 프록시 객체를 초기화 할 때, 프록시 객체가 실제로 엔티티로 바뀌는 것은 아니다
- 정확히 말하면 target에 값이 채워지는 것
- 프록시 객체와 상속받고 있는 객체는 타입이 다르다. 타입을 체크할 떄 주의해야 한다.
- 타입 체크를 위해 instanceOf를 사용해야 한다.
- 영속성 컨텍스트에 이미 찾는 엔티티가 있다면, getReference()메서드로 호출해도 실제 엔티티를 반환한다. 굳이 존재하는 객체를 다시 Proxy객체로 래핑하는게 성능상 이점이 없기 때문
- JPA는 하나의 영속성 커텍스트에 조회하는 같은 엔티티의 동일성을 보장한다.
현업에서 겪는 문제
- 준영속 상태일때, 초기화 문제에서 발생
- 트랜잭션 범위 밖의 프록시 객체를 조회하려 할 떄 발생한다.
- 하이버네이트는 LazyInitiailizationException예외를 발생시킨다.
- 이를 해겨하기 위해 open-in-view설정을 true로 가져간다.
- 영속성 컨텍스트를 뷰 렌더링하는 시점까지 유지시키는 방법
- em.detach(준영속상태로 만드는 것,em.close(),em.clear 모두 같은 예외 발생
- 프록시 객체를 초기화 할 수 없다. 더 이상 영속성 컨텍스트의 도움을 받지 못한다.예시 코드
@SpringBootTest @Transactional @Rollback(value = false) class ProxyTest { @Autowired EntityManager em; @Test public void checkProxy(){ Member member = new Member(); member.setUsername("memberA"); em.persist(member); em.flush(); em.clear(); Member findMember = em.getReference(Member.class, member.getId()); //준영속상태로 만들기 em.detach(findMember); System.out.println("findMember = " + findMember.getUsername()); } }
결과
- LazyInitializationException 예외가 발생
- could not initialize proxy 라고 뜬다. 영속성 컨텍스트에서 관리하는 객체가 아니기 때문
org.hibernate.LazyInitializationException: could not initialize proxy [study.jpa.queryDSL.domain.Member#1] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:322)
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45)
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
at study.jpa.queryDSL.domain.Member$HibernateProxy$5vcprY27.getUsername(Unknown Source)
at study.jpa.queryDSL.domain.ProxyTest.checkProxy(ProxyTest.java:34)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
프록시 초기화 상태 확인
- EntityManagerFactory를 호출하여 getPersistenceUnitUtil().isLoaded(Entity)를 사용하면 초기화된 상태인지 확인할 수 있다.
@Autowired
EntityManager em;
@PersistenceUnit
EntityManagerFactory emf;
@Test
public void checkProxy(){
Member member = new Member();
member.setUsername("memberA");
em.persist(member);
em.flush();
em.clear();
Member findMember = em.getReference(Member.class, member.getId());
//준영속상태로 만들기
em.detach(findMember);
//proxy의 초기화 상태를 확인할 수 있다.
boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember);
System.out.println("loaded = " + loaded);
}
결과
- 초기화되지 않은 객체임을 확인 가능
/* insert study.jpa.queryDSL.domain.Member */ insert into member (created_by, created_date, last_modifed_by, last_modifed_date, age, team_id, username, member_id) values (?, ?, ?, ?, ?, ?, ?, ?)
/* insert study.jpa.queryDSL.domain.Member */ insert into member (created_by, created_date, last_modifed_by, last_modifed_date, age, team_id, username, member_id) values (NULL, NULL, NULL, NULL, 0, NULL, 'memberA', 1);
loaded = false
728x90
반응형
'Spring > JPA' 카테고리의 다른 글
@Where Deprecated되고 새로 쓰이는 @SQLRestriction (1) | 2024.11.20 |
---|---|
JPA - DB 연결 예외 : 'url' attribute is not specified and no embedded datasource could be configured. (0) | 2024.06.14 |
JPA - 지연로딩과 즉시로딩 (1) | 2023.12.10 |
JPA - MappedSuperClass (0) | 2023.12.10 |
JPA - 상속관계매핑 (0) | 2023.12.09 |