[JPA] API를 만들 때 다시 보는 글

SJ·2022년 12월 31일

JPA

목록 보기
12/12

🔎 API 개발을 할 때 주의할 점을 정리한 글입니다.


📍엔티티를 노출시키지 말자

💡 컨트롤러에서 파라미터를 엔티티로 바로 받지 말고, 중간에 API 스펙에 맞는 DTO를 만들어서 활용하자.

💡 양방향 연관관계일 경우, 한쪽에 @JsonIgnore를 하지 않으면 무한루프에 빠질 수 있다.

xToOne 관계에서의 최적화

📍연관관계 fetch를 LAZY로 설정하고, 필요할 때에만 페치 조인을 사용하자.

💡 물론 LAZY, EAGER 둘 다 N+1 문제를 야기시킬 수 있다.
💡 지연 로딩의 경우 , 조회한 order에 대해 member나 delivery를 필요로 하는 경우에 각 order별로 member나 delivery 조회 쿼리가 N번씩 나가게 된다.
💡 그러나 지연 로딩은 필요시에 DB에 바로 조회하지않고, 영속성 컨텍스트를 먼저 확인하기 때문에, 한번 조회했던 member에 대해서 쿼리가 하나 줄어들 수 있다.
💡 즉시 로딩의 경우, order를 조회후, 곧바로 연관관계에 있는 member와 delivery를 조회하기 위해 쿼리가 한꺼번에 나가므로, 이 때 예상치 못한 쿼리가 나가거나, 성능을 저하시킨다.

📍위의 N+1 문제를 해결하기 위해서는 어떻게 해야할까?

💡 페치 조인을 사용하자
em.createQuery(" select o from Order o " +
" join fetch o.member m " +
" join fetch o.delivery d ").getResultList();
연관관계 페치를 LAZY로 설정해도, 위처럼 페치 조인을 사용한다면 쿼리 한번에 모든 객체의 값을 채워서 가져올 수 있다.

📍쿼리 방식 선택 권장 순서

  1. 엔티티를 DTO로 변환하는 방법 선택
  2. 필요할 때 페치 조인으로 성능 최적화 -> 대부분 이슈 해결 가능하다.
  3. 그래도 안되면 DTO로 직접 조회하기
  4. 최후의 방법 -> JPA가 제공하는 네이티브SQL, 스프링 JDBC Template 사용해서 SQL 직접 사용하기

OneToMany 컬렉션 조회에서 페이징을 하는 방법

  • 컬렉션을 페치조인할 경우 일대다 페치조인이 발생하여 데이터가 예측 할 수 없이 증가한다(N+1문제)

  • 일대다에서는 일(1)을 기준으로 페이징을 해야하지만, 데이터는 다(Many)를 기준으로 row가 생성된다.

  • 그래서 Order를 기준으로 페이징하고싶어도 다인 OrderItem이 기준이 되어버린다.

  • 위 문제들을 해결하기 위해서, 우선 ToOne(OneToOne,ManyToOne) 관계를 모두 페치조인한다.(row수를 증가시키지 않는다.)

  • setMaxResult(limit), setFirstResult(offset)을 사용한다.

  • jpa.properties.hibernate.default_batch_fetch_size:
    설정한 수 만큼 미리 데이터를 조회해둔다. 내가 설정한 수가 10, 총 데이터가 100일 경우
    in 쿼리가 10번 나간다.
    지연 로딩으로 발생해야 할 쿼리를 IN으로 한번에 모아 보내는 기능

-> ToOne 관계는 페이징시 row수를 증가시키지 않으므로 페치 조인하고, 컬렉션은 ToMany 관계이므로 default_batch_fetch_size를 사용한다.(maximum : 1000)

profile
slowly But Surely

0개의 댓글