자라선

[자바 ORM 표쥰 JPA 프로그래밍] 15일차 - 프록시 본문

Develop/JPA

[자바 ORM 표쥰 JPA 프로그래밍] 15일차 - 프록시

자라선 2021. 9. 1. 19:21

DB에서 엔티티를 조회할때 항상 모든 데이터를 조회하는 것은 아니다.

JPA는 성능 향상을 위해 필요한 데이터만 DB로 통해 SELECT 를 하게 된다.

 

하지만 성능 향상을 위하여 관계가 있는 모든 엔티티를 조회하지는 않는다. 

이는 지연로딩인 LAZY 와 관련이 있다.

 

1. 프록시

1:N

TEAM 테이블의 데이터만 가져와 매핑된 엔티티로 조회하려고 한다.

하지만 우리는 Team 엔티티만 필요하지 Member 엔티티까지 불러올 필요는 없다. 

더군다나 1:N 관계이다보니 Team에 관계돤 모든 Member를 가져올려면 성능이 좋지못할 것이다.

 

이럴때 JPA는 성능 향상을 위해 Team 엔티티만 조회하게 된다.

final Team team = em.find(Team.class, 1l);  // Team 엔티티만 조회함!

-- TEAM TABLE 만 SELECT 함
Hibernate: 
    select
        team0_.TEAM_ID as TEAM_ID1_3_0_,
        team0_.name as name2_3_0_ 
    from
        Team team0_ 
    where
        team0_.TEAM_ID=?

 

실제 위와같이 코드를 실행하게 되면 JPA는 TEAM 테이블 SELECT 절만 출력하게 된다.

 

그리고 만약 Member 엔티티도 조회하고 싶다면 추가로 가져오면 된다.

        final List<Member> members = team.getMembers();
        for (Member m : members) {
            System.out.println("m = " + m.getName());
        }
        
-- MEMBER 테이블도 조회
Hibernate: 
    select
        members0_.TEAM_ID as TEAM_ID3_3_0_,
        members0_.MEMBER_ID as MEMBER_I1_0_0_,
        members0_.MEMBER_ID as MEMBER_I1_0_1_,
        members0_.name as name2_0_1_,
        members0_.TEAM_ID as TEAM_ID3_0_1_ 
    from
        Member members0_ 
    where
        members0_.TEAM_ID=?

이렇게 출력할때마다 DB로 통해 SELECT 를 하게되는것을 보고 LAZY 로딩이라 부르는데 

team.getMembers() 를 사용하기 위해서는 JPA는 프록시를 우선  넣어주게된다.(아예 비어있는 null로 get 참조하면 NPE 나니깐...)

 

이러한 프록시 객체는 바로 호출하는것도 가능하다.

        // EntityManger 인스턴스에서 getReference 를 사용하여 조회하면 바로 프록시를 가져온다.
        // 하지만 DB에서 데이터를 가져와 엔티티와 매핑한것이 아니라 바로 영속성 컨텍스트에만 넣어줬다.
        final Team team = em.getReference(Team.class, 1l);
        
        // 출력하면 jpa.entity.Team_$$_jvst53c_1 라는 프록시 객체의 클래스가 출력된다.
        System.out.println(team.getClass().getName());

EntityManager.getReference() 를 사용하여 바로 프록시를 호출하는것이 가능하다.

하지만 엔티티에 상속된 프록시 객체라 실제 DB에 거치치않아 아무런 데이터도 없는 상태이다.

그렇기 때문에 프록시를 호출하면 SQL를 작성하지않는다.

 

프록시 객체는 상속된 엔티티와 동일한 필드를 가지고있다.

 

2. 프록시 초기화

프록시의 안에는 아무런 데이터를 가지고있지 않지만 기존 엔티티와 비슷하게 사용할수 있다.

        final Team team = em.getReference(Team.class, 1l);
        final String name = team.getName(); // 프록시 초기화

프록시를 호출하여 getName 을 호출하면 해당 프록시가 초기화 되어 SQL를 생성해 데이터를 넣어준다.

똑같이 getter 를 사용하여 다른 필드 또한 가능하다.

 

프록시는 한번만 초기화가 되며 초기화가 된다고 엔티티로 바뀌진않는다.

또 프록시 호출시 이미 영속성 컨텍스트에 엔티티가 있다면 해당 엔티티를 그대로 가져와 실제 엔티티가 된다.

또한 당연하지만 프록시 또한 영속성 컨텍스트가 필요하므로 준영속상태에서 초기화시 오류가 발생한다.

 

 

3. 프록시 확인

프록시 여부를 확인할 수 있는 메소드도 있다.

EntityManagerFactory.getPersistenceUnitUtil 에 해당 메소드가 있다.

        final Team team = em.getReference(Team.class, 1l);
        
        // 프록시 검사 메소드. false = 프록시이다.
        final boolean loaded = entityManagerFactory.getPersistenceUnitUtil().isLoaded(team);
        System.out.println(loaded);

당연한 말이지만 프록시초기화한다고 엔티티가 되지않기 때문에 false 가 true 가 되진않는다..

Comments