Backend

[JPA] 프록시와 즉시로딩, 지연로딩 / 영속성 전이와 고아객체

연_우리 2022. 2. 21. 22:18
반응형

목차

     

     

    em.getReference() 메서드

    @Entity
    public class Member{
        @Id @GeneratedValue
        @Column(name="member_id")
        private Long id;
        private String name;
        @ManyToOne
        @JoinColumn(name="team_id")
        private Team team;
        
        private static void printMember(Member member){
        	System.out.println("member : " + member);
        }
        
        private static void printMemberAndTeam(Member member){
        	System.out.println("member : " + member);
            System.out.println("team : " + member.getTeam());
        }
    }
    
    @Entity
    public class Team{
        @Id @GeneratedValue
        @Column(name="team_id")
        private Long id;
        private String name;
    }

    Member를 DB에서 찾아와서 출력한다고 하자.

    Team의 내용을 출력한다면 printMemberAndTeam()을 사용해야한다.

    Team의 내용을 출력하지 않는다면 printMember()를 사용해야한다.

     

    printMember를 사용할 때 JPA에서 Team의 내용을 가져오는데 사용하지 않는다면 자원의 낭비 아닐까?

    이럴 때 사용하는게 프록시이다.

     

     

    JPA는 DB에서 실제 엔티티를 조회하는 em.find()메서드와

    조회를 최대한 미루기 위해 사용하는 가짜(프록시) 엔티티를 조회하는 em.getReference()메서드를 제공한다.

    em.getReference는 DB에 쿼리가 안나가는데 조회가 되는 것이다.

     

    Member member = new Member();
    member.setName("hello");
    em.persist(member);
    em.flush();
    //insert문 전송
    em.clear();
    
    Member findMember = em.getReference(Member.class, member.getId());
    
    System.out.println("findMember : " + findMember.getClass());
    //findMember : class hellojpa.Member$HibernateProxy$nMvq2opM
    
    System.out.println("findMember.name : " + findMember.getName());
    //select문 전송
    //findMember.name : hello

    getReference를 사용하고 클래스를 조회해보면 $HibernateProxy$~~ 라는 클래스가 조회된다.

     

     

     

    프록시란?

     - 실제 클래스를 상속받아서 만들어진다.

     - 실제 객체의 참조(target)을 보관한다.

     - 프록시 객체를 호출하면, 프록시는 실제 객체의 메소드를 호출한다.

     - 프록시 객체는 처음 사용할 때 한번만 초기화된다.

     - 초기화는 영속성 컨텍스트를 통해 DB에서 조회 후 실제 엔티티를 생성하여 프록시에게 연결하는 작업이다.

     - ==비교 대신 instance of 를 사용해야한다.

     - 프록시가 초기화된다 해도 실제 엔티티로 바뀌는 것은 아니다. 프록시를 통해 엔티티에 접근하는 것이다.

     - 영속성 컨텍스트에 찾는 엔티티가 있다면 em.getReference()를 호출해도 실제 엔티티 반환한다.

     - 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 LazyInitializationException 예외를 발생시킨다.

     

     

    즉시 로딩 : FetchType.EAGER

    printMemberAndTeam()처럼 Member와 Team이 연결되어있을때

    항상 Member와 Team을 조인해서 조회하는 방법이다.

    주의!!

     - 가급적 지연 로딩만 사용하자.

     - 즉시 로딩을 적용하면 연관관계의 모든 테이블을 조인해서 가져오기때문에

        Member만 출력한다해도 JPA에서는 모든 테이블을 조회한다

     - 즉시로딩은 JPQL에서 N+1문제를 일으킨다. 

       select m from Member m 했을 때, Member를 가져왔더니 Team이 연결되어있다

       앗! Team도 조회해야해! 하고 Team을 조회하는 쿼리를 전송하게된다. (Select문이 2번 호출됨)

     

     

    지연 로딩 : FetchType.LAZY

    printMember()처럼 Member와 Team이 연결되어있지만

    Member에 대한 것만 먼저 조회하고, Team은 필요할 때가 되어서야 조회하는 방법이다.

     

     

     

    영속성 전이 CASCADE

    특정 엔티티를 영속상태로 만들 때, 연관된 엔티티도 함께 영속상태로 만드는 것

    부모 엔티티를 저장할 때, 자식 엔티티도 함께 저장하고 싶을 때 사용한다.

    cascade = CascadeType.PERSIST

     

    게시판 - 첨부파일 관계처럼 "게시판"에서만 "첨부파일"을 관리할 때 사용하면 유용하다.

    첨부파일을 다른 엔티티에서도 관여하게된다면, 사용하지 말자

     

     

     

    고아객체

    부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제한다.

    orphanRemoval = true

     

    parent.getChildList().remove(0) 하는 경우

    삭제된 child에 대해 Delete쿼리가 전송된다.

    반응형
    • 네이버 블러그 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 구글 플러스 공유하기
    • 카카오톡 공유하기