프록시 기초
JPA에는 데이터를 가져올 때 em.find() 뿐만 아니라 em.getReference()라는 메소드가 존재한다.
- em.find(): 데이터베이스를 통해서 실제 엔티티 객체 조회
- em.getReference(): 데이터베이스 조회를 미루는 가짜(프록시)엔티티 객체 조회(데이터를 조회시 select 쿼리가 나가지 않음)
public class JPAMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); //트랜잭션 시작
try {
Member member = new Member();
member.setCreatedBy("kim");
em.persist(member);
em.flush();
em.clear();
Member foundMember = em.getReference(Member.class, member.getId());
System.out.println("foundMember's Id = " + foundMember.getId());
tx.commit();
}catch (Exception e){
tx.rollback();
}finally {
em.close();
}
emf.close();
}
}
위에 코드를 실행해보면 foundMember에서 select쿼리가 나가는 것이 아닌 foundMember.getId()를 실행할 때, 즉 실제로 사용될 때 select쿼리가 나간다.
프록시 특징
- 실제 클래스를 상속 받아서 만들어짐
- 실제 클래스와 겉 모양이 같다.
- 사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨(이론상)
- 프록시가 내부적으로 라이브러리를 이용하여 만든다
- 프록시 객체는 실제 객체의 참조(target)를 보관
- 프록시 객체를 호출하면 프록시ㅣ 객체는 실제 객체의 메소드 호출
프록시 객체의 초기화
Member member = em.getReference(Member.class, "id1");
member.getName();
1. getName() 호출을 할 때 처음에는 Member target에 값이 없다는 것을 인지한다
2. 영속성 컨텍스트에 초기화를 요청을 한다.(진짜 값 요청)
3. DB를 조회한다.
4. 실제 Entity를 생성한다.
5. Proxy 내부에 있는 target과 entity를 연결시켜준다.
프록시 특징
- 프록시 객체는 처음 사용할 때 한 번만 초기화
- 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아님, 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능
- 프록시 객체는 원본 엔티티를 상속받음, 따라서 타입 체크시 주의해야함(== 비교실패, 대신 instance of 사용)
- 연속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환 - 이미 member는 1차 캐시에 있기 때문에 Proxy로 가져올 필요가 없기 때문 - JPA에서는 ==비교가 한 영속성 컨텍스트에서 가져온 객체이며, PK가 같으면 항상 true를 보장해야 한다. 따라서 proxy로 객체를 다시 가져오게 되면 == 비교시 false가 나오기 때문에 실제 엔티티를 반환하는 것이다.
- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화 문제 발생 (하이버네이트는 org.hibernate.LazyInitialization 예외를 터트린다.)
프록시 확인
- 프록시 인스턴스의 초기화 여부 확인 - emf.PersistenceUnitUtil.isLoaded(Object entity)
- 프록시 클래스 확인 방법 - entity.getClass().getName() 출력(..javasist.. or HibernateProxy...)
- 프록시 강제 초기화 - org.hibernate.Hibernate.initialize(entity);
- 참고: JPA표준은 강제 초기화 없음 - 강제 호출: member.getName()
프록시를 공부한 이유
getReference는 잘 쓰이지 않는다. 그럼에도 불구하고 공부한 이유는 proxy의 메커니즘을 이해해야 즉시 로딩과 지연 로딩에 대해서 이해가 가능하기 때문이다
'JPA' 카테고리의 다른 글
영속성 전이 :CASCADE (0) | 2022.10.31 |
---|---|
즉시 로딩과 지연 로딩 (0) | 2022.10.31 |
쇼핑몰 만들기 3 - 상속관계 매핑 (0) | 2022.10.30 |
@MappedSuperclass (0) | 2022.10.30 |
상속관계 매핑 (0) | 2022.10.29 |