JPA N+1問題について

3350 ワード

JPAを使うといろいろな問題があります
今日はその代表的な問題N+1について解説します.

概要


JpaRepositoryでエンティティのリストを問い合わせると、リストに存在する数に関連付けられたエンティティを問い合わせることができます.
@Entity
@Getter
@Table(name = "products")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private long price;

    @Getter(AccessLevel.NONE)
    @Embedded
    private ProductImages productImages;

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "product")
    private List<Review> reviews;

    ...
}

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Review {
    @Id
    private Long id;

    @Column(name = "user_id")
    private String reviewer;

    private String content;

    @ManyToOne
    private Product product;

    ...
}



public interface ProductRepository extends JpaRepository<Product, Long> {
}



@SpringBootTest
class ProductRepositoryTest {

    @Autowired
    ProductRepository productRepository;

    @Test
    void N_1_문제_테스트() {
        // given
        Product product_1 = Product.of("product_1", 30000, asList(ProductImage.of("imagePath", 0)));
        Product product_2 = Product.of("product_2", 20000, asList(ProductImage.of("imagePath", 0)));
        productRepository.save(product_1);
        productRepository.save(product_2);

        // when
        productRepository.findAll();
    }
}

まず、Joinクエリーは予想通りに実行されず、製品クエリーとReviewクエリーがそれぞれ実行されていることがわかります.
ここでは、製品数と同じレビュークエリーが実行されていることがわかります.

Why ?


なぜこのような問題が発生したのですか?
まず、問題は次のコードに現れます.
...
public class Product {
	
	...

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "product")
    private List<Review> reviews;

    ...
}
この部分を見ると現在のロードポリシーはEAGERロード
JPAのグローバル・フェッチ・ポリシーは、単一のEntityを問合せた場合にのみ有効になります.
「エンティティ」リストをすぐに適用するのではなく、「エンティティ」リストをPersistContextにアップロードします.
適用されます.
したがって、EntityリストがPersistContextにすべてアップロードされ、EAGER fetchポリシーであることを確認してください.
エンティティのリストを表示すると、エンティティのリストに存在するエンティティの関連エンティティが表示されます.
結果はN+1問題を引き起こす.

Resolve


Fetch joinの使用

  • JPA Queryメソッドを使用する場合
  • Querydslを使用
  • Fetch joinはページングクエリを実行しません

    @EntityGraphを使用した位置決め



    @BaschSizeノイズの使用