LazyInitializationException в Spring Boot + HibernateОдна из самых частых ошибок при работе с JPA:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection
📌 Причина: лениво загружаемая коллекция (
LAZY) обращается к БД вне транзакции — например, в слое контроллера или после закрытия Session.💡 Как избежать?
✅ Решение 1:
@Transactional в сервисеУбедитесь, что вы обращаетесь к ленивым коллекциям внутри метода с
@Transactional:
@Transactional
public UserDto getUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow();
// OK: коллекция friends будет инициализирована в транзакции
return new UserDto(user.getName(), user.getFriends());
}
⚠️ Не используйте
@Transactional в контроллерах - это плохая практика.✅ Решение 2: Fetch Join
Подгрузите нужные данные сразу через
JOIN FETCH:
@Query("SELECT u FROM User u LEFT JOIN FETCH u.friends WHERE u.id = :id")
Optional<User> findByIdWithFriends(@Param("id") Long id);
📌 Плюс: 1 запрос вместо N (N+1 проблема решается).
📌 Минус: может быть избыточная загрузка, особенно с большими коллекциями.
✅ Решение 3: DTO проекция
Лучший способ в большинстве случаев - проецировать сразу в DTO:
@Query("""
SELECT new com.example.UserDto(u.name, f.name)
FROM User u
LEFT JOIN u.friends f
WHERE u.id = :id
""")
List<UserDto> findUserWithFriendNames(@Param("id") Long id);
📌 Выгружает только нужные данные. Быстро, безопасно, эффективно.
Ленивая инициализация - ок, если вы контролируете границы транзакций.
Проекции и
fetch join - ваши лучшие друзья, если нужен контроль и производительность.📲 Мы в MAX
👉@BookJava
