JPA 프록시

송현진·2023년 7월 18일
0

Jpa

목록 보기
6/9
post-thumbnail

프록시("대신한다")

  • em.find() : 데이터베이스를 통해 실제 엔티티 객체 조회
  • em.getReference() : 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회(DB에 쿼리 안날아가는 데 조회됨)
  • 프록시 객체는 실제 객체의 참조를 보관(동작 시켜주는 가짜 객체)
  • 프록시 객체 호출하면 프록시 객체는 실제 객체 메소드 호출
  • 처음 사용할 때 한 번만 초기화
    • 영속성 컨텍스트의 도움을 받아야 함(실제 엔티티가 아니기때문)
    • 준영속 상태의 프록시를 초기화하면 예외가 발생
    • 준영속 상태일 때 프록시를 초기화하려면, 영속성 컨텍스트에 다시 엔티티를 영속화시켜야 하며, 이때는 em.merge() 메서드를 사용하여 엔티티를 병합한 후 프록시를 초기화해야 한다
  • 프록시가 실제로 바뀌는 것이 아니라 초기화되면 실제 엔티티에 접근 가능해 진다.
  • 원본 엔티티 상속받음, 타입 체크시 주의해야됨!!(== 비교 실패, 대신 instance of 사용)
private static void logic(Member m1, Member m2){
	System.out.println("m1 == m2 : " + (m1 instanceof Member));
    System.out.println("m1 == m2 : " + (m2 instanceof Member));
}
  • 영속성 컨텍스트에 찾는 엔티티 이미 있으면 em.getReference()를 호출해도 실제 엔티티 반환
Member m1 = em.find(Member.class, m1.getId());
System.out.println("m1 = " + m1.getClass());

Member reference = em.getReference(Member.class, reference.getId());
System.out.println("reference = " + reference.getClass());

System.out.println("a == a " + (m1 == reference)); // true

Member refMember = em.getReference(Member.class, refMember.getId());
System.out.println("refMember = " + refMember.getClass());

Member findMember = em.find(Member.class, findMember.getId());
System.out.println("findMember = " + findMember.getClass());

System.out.println("a == a " + (m1 == reference)); // true

✏️ 둘 다 Member 객체를 반환, 한 트랜잭션 안에서 항상 true를 반환한다.
✏️ 그렇기 때문에 프록시로 먼저 조회하면 프록시 객체를 반환하고 select 쿼리문이 나가고 findMember를 해도 항상 true를 만들어주기 위해 프록시 객체를 반환시켜준다.

  • 영속성 컨텍스트에 도움 받을 수 없는 준영속(detach(변수) 일 때, 프록시 초기화하면 문제 발생(close(), clear()도 똑같이 에러발생)

프록시 객체 초기화

Member member = em.getReference(Member.class, "id1");
member.getName();
  • 최초 지연 로딩 시점엔 당연히 참조 값이 없다. 그렇기에 실제 객체의 메서드를 호출할 필요가 있을 때 데이터베이스를 조회해서 참조 값을 채우게 되는 것
  • id를 조회할 때는 프록시가 초기화 되지 않는다(식별자 조회)

프록시 확인

EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();

EntityTransaction tx = em.getTransaction();
tx.begin();

emf.getPersistenceUnitUtil().isLoaded(프록시객체); // 초기화 여부 확인
프록시객체.getClass(); // 클래스 확인
프록시객체.getUsername(); // 강제 초기화
Hibernate.initailize(프록시객체) // 강제 초기화
profile
개발자가 되고 싶은 취준생

2개의 댓글

comment-user-thumbnail
2023년 7월 18일

정보가 풍부해서 많은 도움이 되었습니다.

답글 달기
comment-user-thumbnail
2023년 7월 18일

글이 잘 정리되어 있네요. 감사합니다.

답글 달기