Sobes.tech
Junior — Middle

Magyarázza el, miért fordulhat elő N+1 probléma a UserRepository.findById() és a User.getEmails().forEach() módszerek végrehajtásakor, ha a getUsers() metódus @Transactional-ként van megjelölve, és az email mező @OneToMany-ként van annotálva.

sobes.tech MI

Válasz az MI-től

Az N+1 probléma a JPA-ban lévő kapcsolatok lustán történő betöltése miatt jelentkezik. Az esetében:

  • A UserRepository.findById() metódus betölti a felhasználót.
  • Az emails mező @OneToMany-ként van megjelölve, alapértelmezés szerint a betöltés lustán történik (FetchType.LAZY).
  • A User.getEmails().forEach() hívásakor minden felhasználóhoz külön kérés történik az adatbázisból, hogy betöltse az email-jeit.

Ha a getUsers() metódus @Transactional-ként van megjelölve, a session nyitva van, és a lustán betöltött kollekciók betölthetők hozzáféréskor. De ha végigiterálsz a felhasználók listáján, és minden esetben hívod a getEmails()-t, akkor 1 kérés a felhasználókra + N kérés az email-ekre — innen ered az N+1 probléma.

Ennek elkerülésére használhatod:

  • JOIN FETCH JPQL-ben a kollekciók előzetes betöltéséhez.
  • A betöltési stratégia módosítása EAGER-re (óvatosan).
  • Entity Graphs használata.

Példa JOIN FETCH-tel:

@Query("SELECT u FROM User u JOIN FETCH u.emails WHERE u.id = :id")
User findByIdWithEmails(@Param("id") Long id);