• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

다대일에서 조인과 그렇지 않은 상황에서의 속도 차이

22.11.08 04:33 작성 조회수 214

0

안녕하세요 JPA 강좌를 잘 수강하고 있는 와중에 궁금한 점이 생겨서 질문올립니다.

 

먼저 궁금한 것은 다대일에서 조인입니다.

 

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
public class Category implements Serializable{
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    
    @OneToMany(mappedBy = "category", cascade = CascadeType.PERSIST)
    private List<Product> products = new ArrayList<>();

    public void addProducts(Product product) {
        products.add(product);
        product.setCategory(this);
    }
}
@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Product implements Serializable {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Integer price;

    @ManyToOne
    @JoinColumn(name="category_id")
    private Category category;

    public void update(Product source) {
        this.name = source.getName();
        this.price = source.getPrice();
    }

    public void setCategory(Category category) {
        this.category = category;
    }
}

 

위와 같은 예제 코드 상황에서

Product의 입장에서 findById를 하게 되면

@JoinColumn

어노테이션이 붙어서 자연스럽게 join한 쿼리가 날라가게 됩니다.

 

하지만 findByCategoryName 메서드를 실행하면

조인 쿼리가 아니라

select p from Product p where p.category.name = ?1

이런 where절 쿼리, 즉 natural join 쿼리가 발생하는데요,

 

JPA에서 자동으로 조인을 하지 않고 기본 SQL순서인

from -> where -> select 으로 진행될텐데,

 

 

select p from Product p

left join category c

on p.category_id = c.id

where c.name = ?1 와 속도 측면에서 얼마나 차이가 나는지 궁금합니다.

(레코드 수에 따라 다르겠지만)

 

 

답변 1

답변을 작성해보세요.

1

김태경님의 프로필

김태경

2022.11.09

위와 같은 예제 코드 상황에서
Product의 입장에서 findById를 하게 되면
@JoinColumn
어노테이션이 붙어서 자연스럽게 join한 쿼리가 날라가게 됩니다.

일단 단건 조회를 하실 때 다대일 관계에서 join 쿼리가 발생하는 이유는 @JoinColumn 덕분이 아니고 @ManyToOne의 기본 로딩 정책이 EAGER이기 때문입니다. LAZY로 변경해보시면 검증하실 수 있습니다.

 

질문하신

select p from Product p where p.category.name = ?1 의 경우 어떠한 경위로 이러한 쿼리가 발생하셨다는 건지 궁금하네요.

먼저 findByCategoryName 메서드 쿼리를 테스트해보니 select 문이 한 번 더 발생하는 것으로 보입니다. 다시 말해 findByCategoryName을 수행하면 select 문에 category 정보를 포함하지 않고, EAGER 로딩에 의해 select가 한 번 더 발생하네요. 설계가 좋고 나쁨을 떠나서 findByCategoryName을 사용하면 이런 상황이 발생하고 있음을 알려 드립니다.

 

그리고 findByCategoryName은 left join을 발생시키는 반면에 select p from Product p where p.category.name = ?1 의 경우 직접 테스트해보시면 inner join이 발생합니다. 따라서 우선은 findByCategoryName 기준으로 비교하겠습니다.

 

findByCategoryName 발생 쿼리문: select p from Product p left join category c on p.category_id = c.id where c.name ?

비교용으로 제시하신 쿼리문 : select p from Product p left join category c on p.category_id = c.id where c.name = ?1

결국 완전히 동일한 쿼리문입니다.

select p from Product p where p.category.name = ?1

위 쿼리 문으로 비교한다 하더라도 inner join이라는 점이 다를 뿐 큰 차이가 없습니다.