15 Aralık 2020 Salı

JPA N+1 Select/Query Problem

Giriş 
Parent nesneler yükleniyor ve döngü ile dolaşılıyor. Her parent nesne için child nesneleri çekecek bir SQL çalıştırılır.

Örnek
Şu kodda N+1 problemi ortaya çıkar
@OneToMany(mappedBy = "doctor", fetch = FetchType.LAZY)
private Collection<Appointment> appointments;
Örnek
Şu kodda N+1 problemi ortaya çıkar
List<Doctor> doctors = entityManager.createQuery("select d from Doctor d",
Doctor.class)
.getResultList();
for (Doctor doctor : doctors) {
  Assert.assertTrue(doctor.getAppointments().size() > 0);
}
Örnek
Şu kodda N+1 problemi ortaya çıkar
List<String> postTitlesStreamRecords = postRepository.findAll()
    .stream()
    .filter(
        post -> post.getTags()
                    .stream()
                    .map(Tag::getName)
                    .anyMatch(matchingTags::contains)
    )
    .sorted(Comparator.comparing(Post::getId))
    .map(Post::getTitle)
    .collect(Collectors.toList())
Çözüm 1
EAGER FETCH yapmak

Çözüm 2
JPQL kullanıyorsak "JOINT FETCH" yapmak. Yani nesne ve ilişkilerini tek seferde yüklemek

JPA JPQL Join yazısına bakabilirsiniz.

Çözüm 3
SQL kullanıyorsak INNER JOIN kullanırız.Şöyle yaparız.
public List<String> findPostTitleByTags(List<String> tags) {
  return entityManager.createNativeQuery("""
    select p.title
      from post p
      where exists (
        select 1
          from post_tag pt
          join tag t on pt.tag_id = t.id and pt.post_id = p.id
          where t.name in (:tags)
      )
      order by p.id
    """)
  .setParameter("tags", tags)
  .getResultList();
}
Çözüm 4

Hiç yorum yok:

Yorum Gönder