스파이더 웹 개발

프록시와 연관관계 관리 본문

JPA

프록시와 연관관계 관리

스파이더웹 2022. 9. 16. 12:01
728x90
반응형

프록시

em.find() 와 em.getReference()

 

em.find() : 데이터베이스를 통해서 실제 엔티티 객체 조회

em.getReference() :  데이터베이스 조회를 미루는 가짜 엔티티 객체 조회(즉, DB의 쿼리가 실행이 안되는데 객체가 조회 된다)

 

em.find()

 try {

            Member member = new Member();
            member.setName("hello");
            
            em.persist(member);
            
            em.flush();
            em.clear();

            Member findMember = em.find(Member.class, member.getId());
            System.out.println("findMember.getId() = " + findMember.getId());
            System.out.println("findMember.getName() = " + findMember.getName());
            tx.commit();
        }

em.find를 했을 경우에는 예상했던대로 정상적으로 select 쿼리를 해서 값을 가져오는것을 알 수 있다.

 

em.getReference()

//            Member findMember = em.find(Member.class, member.getId());
            Member findMember = em.getReference(Member.class, member.getId());
//            System.out.println("findMember.getId() = " + findMember.getId());
//            System.out.println("findMember.getName() = " + findMember.getName());
            tx.commit();

em.getReference() 했을때는 select 쿼리가 실행되지 않는 것을 알 수 있다.

이후 System.out.println의 주석을 풀었을때 실제 객체를 사용할때 select 쿼리가 실행되는데 그이유는 무엇일까?

그 이유가 프록시이다

 

 

프록시는 실제 클래스를 상속받아서 만들어진다. 그러므로 실제 클래스와 겉 모양이 같아 사용자는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다

 

프록시 객체는 실제 객체의 참조를 보관하는데 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다

진행 순서

클라이언트가 getName를 호출하면 처음에는 프록시 객체에 Member target의 값이 없으므로 JPA가 영속성컨텍스트에 Member를 요청한다. 그러면 영속성컨텍스트에서 DB를 조회해서 실제 엔티티를 생성해서 프록시에 target에 연결해준다

이후에는 연결되어있으므로 프록시가 실제 엔티티의 getName을 바로 호출한다

 

프록시 특징

  • 프록시 객체는 처음 사용할 때 한번만 초기화
  • 프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는 것이 아니고 초기화 되면 프록시 객체를 통해서 실제 엔티티에 접근
  • 프록시 객체는 원본 엔티티를 상속 받음
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해도 실제 엔티티를 반환

예시)

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

findMember = class hellojpa.Member$HibernateProxy$lfbRxDAp
======================================

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

find = class hellojpa.Member
reference.getClass() = class hellojpa.Member

엔티티가 없는 상황에서  em.getReference()를 호출하면 프록시 객체가 반환되지만, 

밑에 상황의 경우 em.find()로 영속성컨텍스트에 Member 클래스가 있으므로, 실제 엔티티가 반환된다

그 이유는 같은 영속성컨텍스트 안에있고, 기본키가 같다면 true를 반환해야하기 때문이다

  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일때 프록시를 초기화하면 문제 발생

 

즉시 로딩 과 지연 로딩

예를 들어 ) 현재 예제에서 Member 조회시 Team도 함께 조회가 되는데, 단순히 Member 정보만 사용하는 경우 자원 소모가 발생되는데, 이러한 경우 JPA에서는 지연 로딩을 지원한다

 

사용 방법

@ManyToOne(fetch = FetchType.LAZY) 방식으로 사용하면 된다.

 

지연로딩을 사용할 경우 select 쿼리시 Member만 조회되는 것을 확인할 수 있다.

System.out.println("===============");
find.getTeam().getName();
System.out.println("===============");

이후 Team의 속성을 사용하는 시점에 DB에서 Team을 가져온다

 

만약 Member와 Team을 함께 사용한다면 ?

즉시 로딩을 사용한다

@ManyToOne(fetch = FetchType.EAGER)

member를 가져올때 team도 join해서 값을 가져온다

그러나 가급적 지연 로딩만 사용한다

그 이유는?

  • 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생
  • 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다
  • @ManyToOne, @OneToOne는 기본적으로 EAGER로 설정되어 있어, LAZY로 변경한다

 

영속성 전이 : CASCADE

특정 엔티티를 영속상태롤 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때

예제)

  try {
            Parent parent = new Parent();
            Child child1 = new Child();
            Child child2 = new Child();

            parent.addChild(child1);
            parent.addChild(child2);

            em.persist(parent);
            em.persist(child1);
            em.persist(child2);

            tx.commit();
        }

위와 같은 로직에서 영속상태를 만들어주려면 parent와 child 모두 em.persist() 하여 영속성 컨텍스트로 만들어준다

이러한 방법말고 cascade를 사용하면되는데

  @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
   private List<Child> childList = new ArrayList<>();

사용하게 되면 child를 em.persist()해주지 않아도 영속상태가 되었다.

 

CASCADE는 단일 종속일때만 사용하는 것을 권장한다. 지금처럼 parent와 child만 연관관계일때, 근데 Member가 child와도 연관관계인경우에는 사용하기 어려워진다

 

고아객체

고아 객체 제거 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제

orphanRemoval = true 를 통해 제거한다

728x90
반응형

'JPA' 카테고리의 다른 글

객체 지향 쿼리 언어  (0) 2022.09.16
값 타입  (0) 2022.09.16
고급 매핑  (0) 2022.09.07
다양한 연관관계 매핑  (0) 2022.09.02
연관관계 매핑  (0) 2022.08.30
Comments