개발 이론/JPA

[JPA] 프록시에 대하여

dal_been 2023. 11. 12. 00:36
728x90

 

JPA에 대해 공부하다보면 프록시라는 단어를 알게된다. 이전의 나는 그냥 가짜 객체다 정도까지만 알고 있었다. 이번에는 조금더 자세히 알아보기로 한다

 

JPA를 공부하면 연관관계 로딩 방식중 지연로딩이라는 것을 알게된다. 연관된 엔티티를 실제로 사용하기 전까지 조회하지 않는 것이 지연로딩이다. 이렇게 지연하면 즉시 로딩 방식에 비해 최초 로딩 시간이 빠르고 메모리 소모가 적다.

그런데 어째든 해당 연관 필드에 null이 들어가는 건 좀 그러니 뭘 채워야하는데 그게 바로 가짜 엔티티 프록시 객체이다.

 

 

 

프록시 객체는 엔티티 객체의 상속이다

 

프록시 객체가 엔티티 객체를 대체하지만 초기화후에는 엔티티처럼 작동해야한다. 때문에 엔티티 객체를 상속해서 만들어진다. 이때문에

JPA에서 엔티티 객체의 기본 생성자 접근제한자가 최소 protected이어야한다(이전 블로그 내용에 있다)

 

프록시 객체가 처음 사용될때 한번만 초기화가 되는데 초기화시 엔티티처럼 작동하지만 실제 엔티티로 바뀌는 것이 아니다. 그저 프록시 객체를 통해 실제 엔티티에 접근이 가능할 뿐이다. 정확히 말하면 target값이 채워지는 것 뿐이다.

 

 

그렇다면 프록시 객체와 원본객체 "=="비교시 성공할까??

NOPE! 단순히 상속한 것이지 타입이 같은 것이 아니다.

따라서 타입 비교를 하려면 왠만하면 instance of를 사용해야한다

 

프록시 객체가 생성되면 영속성 컨텍스트는 동일성 보장을 위해 프록시를 반환

 

만약 영속성 컨텍스트에 찾는 엔티티가 있다면 , em.getRefernce()(DB조회를 미루는 가짜 엔티티를 조회하는 메서드)를 호출해도 실제 엔티티를 반환한다.

만약 프록시로 만들어진 엔티티에 대한 조회가 또다시 조회가 들어와도 프록시 객체를 반환한다.

초기화 여부 상관없이 영속성 컨텍스트는 프록시 객체를 반환한다

 

그러나 영속성 컨텍스트에 최초로 저장될때 실제 엔티티로 저장될 경우, 이후로는 프록시가 아닌 실제 엔티티가 반환된다. 이는 지연로딩으로 인해 프록시가 들어갈자리에도 마찬가지이다.

 

 

하이버네이트에서 프록시 getID()는 프록시 초기화하지 않는다

 

프록시 객체는 엔티티 객체의 데이터를 사용할때 초기화된다. 

 

그런데 하이버네이트에서 식별자를 꺼내는 getID를 호출할때는 초기화되지 않는다

 

Team team=new Team("teamA");
em.persist(team);

Member member= new Member("member1");
member.setTeam(team);
em.persist(team);
em.clear();

Member findMember=em.find(Member.class,member.getId());

System.out.println(findMember.getTeam().getId());  // 프록시 초기화 X
System.out.println(findMember.getTeam().getName()); //프록시 초기화 0, select 쿼리 발생

 

하이버네이트는 식별자를 호출할때 엔티티를 초기화하지 않는다. 만약 식별자 호출시 엔티티 초기화를 원한다면 "hibernate.jpa.compliance.proxy"값을 true로 설정해주어야한다

 

다만 주의할점!! 프록시 객체는 기본적으로 모든 필드값을 null로 가지고 있다. 하지만  프록시 객체는 추가로 interceptor(ByteBuddyInterceptor)를 가지고 있는데 이 인터셉터가 id값, 원래 엔티티 타입 정보등을 가지고 있다. 

그러니 프록시 초기화되지 않아도 id값을 알 수 있는 것이다.

 

이후 초기화 되면 target이라는 필드에 select 쿼리의 결과로 생성된 엔티티가 저장된다. 그래서 초기화이전에는 id값을 제외하고 다른 필드 값을 알수 없는 것이다

 

여기서 또 알아야할것! 앞서 말했듯 프록시객체는 실제엔티티의 참조만을 갖고 있기에 프록시가 초기화된다 해도 실제 엔티티로 바뀌지 않는다