Sobes.tech
Junior — Middle

Vysvetlite, prečo môže pri vykonávaní metód UserRepository.findById() a User.getEmails().forEach() dôjsť k problému N+1, ak je metóda getUsers() označená ako @Transactional a pole email je anotované ako @OneToMany.

sobes.tech AI

Odpoveď od AI

Problém N+1 vzniká kvôli lenivému načítaniu vzťahov v JPA. Vo vašom prípade:

  • Metóda UserRepository.findById() načítava používateľa.
  • Pole emails je označené ako @OneToMany, predvolené načítanie je lenivé (FetchType.LAZY).
  • Pri volaní User.getEmails().forEach() pre každého používateľa sa vykoná samostatný dotaz do databázy na načítanie jeho emailov.

Ak je metóda getUsers() označená ako @Transactional, je session otvorená a lenivé kolekcie môžu byť načítané pri prístupe. Ale ak prechádzate zoznam používateľov a pre každého voláte getEmails(), bude 1 dotaz na používateľov + N dotazov na emaily — odtiaľ problém N+1.

Aby ste tomu zabránili, môžete použiť:

  • JOIN FETCH v JPQL na predbežné načítanie kolekcií.
  • Zmeniť stratégiu načítania na EAGER (s opatrnosťou).
  • Používať Entity Graphs.

Príklad s JOIN FETCH:

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