Ответ
Нет, стандартная итерация по лениво загруженной коллекции (FetchType.LAZY) вне управляющей транзакции (или сессии) вызовет исключение LazyInitializationException.
Почему это происходит?
- Коллекция, помеченная как
LAZY, — это прокси-объект, который не содержит реальных данных. - При первой попытке доступа (например, вызов
size()илиiterator()) прокси инициирует запрос к базе данных для загрузки данных. - Если на этот момент сессия Hibernate уже закрыта (транзакция завершена), выполнить запрос невозможно, что приводит к исключению.
Пример проблемы:
@Entity
public class Document {
@OneToMany(fetch = FetchType.LAZY, mappedBy = "document")
private List<Line> lines; // Ленивая коллекция
}
// В сервисе (транзакция завершена после метода)
@Transactional
public Document getDocument(Long id) {
return documentRepository.findById(id).orElseThrow();
}
// Позже, вне транзакции (ВЫБРОСИТ LazyInitializationException)
Document doc = service.getDocument(1L);
for (Line line : doc.getLines()) { // Попытка доступа к ленивой коллекции
System.out.println(line.getText());
}
Правильные решения:
-
Инициализация внутри транзакции (например, с
JOIN FETCH):@Query("SELECT d FROM Document d JOIN FETCH d.lines WHERE d.id = :id") Document findDocumentWithLines(@Param("id") Long id); -
Использование
@Transactionalна методе, где происходит итерация:@Transactional public void processDocument(Long id) { Document doc = documentRepository.findById(id).orElseThrow(); for (Line line : doc.getLines()) { // Доступ внутри транзакции // ... } } -
Явная инициализация с
Hibernate.initialize()(менее предпочтительно):@Transactional public Document getInitializedDocument(Long id) { Document doc = documentRepository.findById(id).orElseThrow(); Hibernate.initialize(doc.getLines()); // Принудительная загрузка return doc; // Коллекция теперь загружена }
Вывод: Доступ к ленивым коллекциям должен происходить в контексте открытой сессии Hibernate (транзакции). Планируйте загрузку необходимых данных на этапе выполнения запроса.