작성
·
301
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
위와 같은 예제 코드 상황에서
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이라는 점이 다를 뿐 큰 차이가 없습니다.