JPA 정리

박동규·2023년 5월 19일
0

정리

연관 관계의 주인

@JoinColumn 을 사용하는 쪽이 연관 관계의 주인이다. @ManyToOne, @ManyToMany와 함께 사용.
@ManyToMany 다대다 연결방식은 지원은 하지만, 사용하지 않는 걸 권장한다.

  • why?
  1. 매핑 정보만 들어가고 추가 정보를 넣을 수 없기 때문
  2. 중간 테이블이 숨겨져 생성되기 때문에 join 등으로 엮이면 예상하지 못한 희안한 쿼리가 발생하기 때문

@JoinColumn(name = "TEAM_ID") name에 들어가는 값은 외래키의 컬럼명이다.
외래키를 가진 테이블. 외래키를 가진 테이블은 N (다) 데이터를 가진 테이블이다.
-> 외래키에 직접적인 영향을 끼친다.

@OneToMany(mappedBy = "team") mappedBy에 들어가는 값은 연결 된 객체의 자바 코드 필드명이다.
mappedBy 속성은 @OneToMany, @OneToOne 에만 존재한다.
-> 연결된 테이블의 조회만 가능하다.

@ManyToOne, @ManyToMany 다대~ 연관관계의 경우, mappedBy 속성 자체가 존재하지 않는다.
테이블의 외래키는 N (다) 쪽에 존재하기 때문이다.


지연 로딩과 즉시 로딩

JPA 사용 시 2개의 엔티티 객체가 대부분의 경우 같이 조회 된다면
이론적으론 즉시 로딩이 유리하다.

하지만 실무에선 지연로딩만 사용하길 권장한다.
JPQL을 사용할 때 즉시 로딩을 사용하면 N+1 문제가 발생하기 때문이다.

N+1 문제란?

연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오는 현상

ex) Member - Team 에 연관관계가 설정되어 있고, Member 엔티티에 Team 객체가 즉시 로딩으로 매핑 되어있다.

엔티티 매니저를 이용한 em.find(memberId) 같은 경우, JPA가 내부적으로 최적화하여 두 테이블을 Join 하여 쿼리 한방에 Member 와 Team 데이터를 가져와 매핑해준다.

하지만 JPQL 을 사용할 때 문제가 생긴다.
select m from Member m 등의 형태로 쿼리를 날린다면, 순수한 그대로의 SQL로 번역된다.

ex)

        // em.find() 대신 JPQL을 직접 사용하는 경우
        // member만 가지고 온다.
        List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();

        // member만 가지고 와보니 team에 즉시 로딩이 걸려있는 걸 확인한다.
        // 이 과정에서 생기는 것이 n+1 문제이다.

때문에 Member 데이터를 가져오는 쿼리를 1번 수행하여 전체 데이터를 가져온 뒤,
Member 엔티티에 매핑하고 보니 Team이 즉시 로딩이네? 확인하여

다시 select t from Team t where member_id = ???(조회된 Member의 id)
조회된 Member 데이터의 갯수만큼 위의 쿼리를 추가로 날려 Team 데이터를 가져오는 것이다.

Member를 조회하기 위해 최초 1개의 쿼리를 날렸지만,
Team을 조회하기 위해 조회된 Member 데이터의 갯수, N개 만큼의 추가 쿼리가 나가는 것이다.

이를 N+1 문제라 한다.

  • fetch 전략을 EAGER로 설정하여도, JOIN이 일어나지 않고 쿼리가 +N번 추가로 날아간다.

  • em.find()는 PK를 찍어서 가져오는 것이기 때문에 JPA가 내부적으로 최적화를 할 수 있다.

  • 하지만 JPQL은 코드에 적은 쿼리가 그대로 날아가기 때문에 정직하게 Member만 조회한다.

  • Member를 가지고 와봤더니 Team이 즉시 로딩으로 설정되어 있다.

  • 즉시 로딩은 가져올 때 무조건 값이 다 들어가있어야 한다.

  • 따라서 EAGER로 되어있는 Team을 조회하기 위해 10개의 쿼리가 다시 나간다.

  • Member가 10개라면, Member 조회 쿼리 한 번 나가고, 각 10개 Member의 Team을 다시 쿼리한다.

참조 : https://dodeon.gitbook.io/study/kimyounghan-orm-jpa/08-proxy/02-eager-and-lazy

0개의 댓글