본문 바로가기

SpringBoot

[JPA] EntityGraph를 더 잘 써보자

EntityGraph를 이용해서 쿼리를 조금이라도 더 줄여보자


Reservation Entity

@Entity
@Getter
@NoArgsConstructor
public class Reservation extends BaseEntity {

    @ManyToOne(fetch = FetchType.LAZY)
    private Product product;
    private String uniqueId;
    @ManyToOne(fetch = FetchType.LAZY)
    private Member member;
    private String phoneNumber;
    private LocalDateTime checkIn;
    private LocalDateTime checkOut;
    private String status;
    private int isCancel;
    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "review_id")
    private Review review;
    @OneToOne
    @JoinColumn(name = "cancel_id")
    private Cancel cancel;

    @Builder
    public Reservation(Product product, Member member,
                       String phoneNumber, LocalDateTime checkIn, LocalDateTime checkOut,
                       String status, int isCancel) {
        this.product = product;
        this.member = member;
        this.uniqueId = makeUniqueId();
        this.phoneNumber = phoneNumber;
        this.checkIn = checkIn;
        this.checkOut = checkOut;
        this.status = status;
        this.isCancel = isCancel;
    }

    public void setReview(Review review) {
        this.review = review;
    }

    public void deleteReview() {
        this.review = null;
    }

    public void setIsCancelOn(Cancel cancel) {
        this.isCancel = 1;
        this.cancel = cancel;
    }

    public String makeUniqueId() {
        DecimalFormat decimalFormat1 = new DecimalFormat("0000");
        DecimalFormat decimalFormat2 = new DecimalFormat("00");

        String uniqueId = LocalDate.now().getYear() + "-" +
                decimalFormat2.format(LocalDate.now().getMonthValue()) +
                decimalFormat2.format(LocalDate.now().getDayOfMonth()) + "-" +
                decimalFormat1.format(Math.random() * 10000);

        return uniqueId;
    }

}

 

Review Entity

@Entity
@Getter
@NoArgsConstructor
@DynamicInsert
public class Review extends BaseEntity {

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "review")
    private Reservation reservation;
    @ManyToOne(fetch = FetchType.LAZY)
    private Product product;

    @Column(columnDefinition = "TEXT", nullable = false)
    @NotBlank
    private String content;

    @NotNull
    private Double totalStar; // 총 평점, 소수 1자리까지

    @NotNull
    private Double starCleanliness; // 청결도, 소수 1자리까지

    @NotNull
    private Double starAccuracy; // 정확도, 소수 1자리까지

    @NotNull
    private Double starLocation; // 위치, 소수 1자리까지

    @NotNull
    private Double starCostEffective; // 가격 대비 만족도, 소수 1자리까지

    @Builder
    public Review(Reservation reservation,
                  Product product,
                  String content, Double totalStar,
                  Double starCleanliness, Double starAccuracy, Double starLocation, Double starCostEffective) {
        this.reservation = reservation;
        this.product = product;
        this.content = content;
        this.totalStar = totalStar;
        this.starCleanliness = starCleanliness;
        this.starAccuracy = starAccuracy;
        this.starLocation = starLocation;
        this.starCostEffective = starCostEffective;
    }

    public void modify(String content, Double totalStar, Double starCleanliness,
                       Double starAccuracy, Double starLocation, Double starCostEffective) {
        this.content = content;
        this.totalStar = totalStar;
        this.starCleanliness = starCleanliness;
        this.starAccuracy = starAccuracy;
        this.starLocation = starLocation;
        this.starCostEffective = starCostEffective;
    }
}

 

 

현재 상황


  • 현재 Reservation Entity와 Review Entity는 주종 관계입니다.
  • review에도 product와의 연관관계를 다대일로 맵핑해두었습니다. 
  • ReviewRepository의 findAllByProductId 메서드의 조회 결과를 보면 left outer join이 두 번 발생합니다. 아래는 발생한 조회 결과입니다.
@EntityGraph(attributePaths = {"reservation"})
Page<Review> findAllByProductId(long productId, Pageable pageable);
Hibernate: 
select 
review0_.id as id1_9_0_,
reservatio2_.id as id1_8_1_, 
review0_.created_at as created_2_9_0_, 
review0_.modified_at as modified3_9_0_, 
review0_.content as content4_9_0_, 
review0_.product_id as product10_9_0_, 
review0_.star_accuracy as star_acc5_9_0_, 
review0_.star_cleanliness as star_cle6_9_0_, 
review0_.star_cost_effective as star_cos7_9_0_, 
review0_.star_location as star_loc8_9_0_, 
review0_.total_star as total_st9_9_0_, 
reservatio2_.created_at as created_2_8_1_, 
reservatio2_.modified_at as modified3_8_1_, 
reservatio2_.cancel_id as cancel_10_8_1_, 
reservatio2_.check_in as check_in4_8_1_, 
reservatio2_.check_out as check_ou5_8_1_, 
reservatio2_.is_cancel as is_cance6_8_1_, 
...
from review review0_ 
left outer join product product1_ on review0_.product_id=product1_.id 
left outer join reservation reservatio2_ on review0_.id=reservatio2_.review_id 
where product1_.id=? order by review0_.id desc limit ?

해결해 보기

  • Reservation에 중복은 없는 구조이고 Review와 일대일 주종 관계이기 때문에 Reservation에 있는 product를 이용하기로 했습니다.
  • Review에 다대일로 맵핑된 product를 제거하고 Repository에 메서드를 @Query 어노테이션까지 사용해서 검색 조건을 설정해 주었다.
  • 조회 쿼리를 보니 무려 2줄이 줄었다!!
@Query("SELECT r FROM Review r JOIN r.reservation rv WHERE rv.product.id = :productId")
@EntityGraph(attributePaths = {"reservation"})
Page<Review> findAllByReservationProductId(long productId, Pageable pageable);
Hibernate: 
select 
review0_.id as id1_9_0_, 
reservatio1_.id as id1_8_1_, 
review0_.created_at as created_2_9_0_, 
review0_.modified_at as modified3_9_0_, 
review0_.content as content4_9_0_, 
review0_.star_accuracy as star_acc5_9_0_, 
review0_.star_cleanliness as star_cle6_9_0_, 
review0_.star_cost_effective as star_cos7_9_0_, 
review0_.star_location as star_loc8_9_0_, 
review0_.total_star as total_st9_9_0_, 
reservatio1_.created_at as created_2_8_1_, 
reservatio1_.modified_at as modified3_8_1_, 
reservatio1_.cancel_id as cancel_10_8_1_, 
...
from review review0_ inner join reservation reservatio1_ on review0_.id=reservatio1_.review_id 
where reservatio1_.product_id=? order by review0_.id desc limit ?

 

 

참고 : https://jojoldu.tistory.com/165#recentEntries(기억보단 기록을)

'SpringBoot' 카테고리의 다른 글

[JPA] @OneToOne 관계에서 EntityGraph 사용하기  (0) 2023.01.14