Что происходит при вызове shop.getProducts() в Hibernate с ленивой загрузкой?
Что проверяют
Понимаете ли вы, что геттер у сущности это не просто чтение поля, и в какой момент реально уходит запрос в базу. По ответу видно, писали ли вы сервисы на ORM или только читали про неё: почти вся боль тут про границы транзакции.
Как отвечать
Сам вызов геттера в базу не ходит. В поле коллекции лежит не ваш ArrayList, а обёртка Hibernate, которая знает владельца и запрос, но пока пуста и помечена как неинициализированная. Запрос уходит при первом обращении к содержимому: перебор, size(), получение элемента, проверка на вхождение.
Дальше развилка по состоянию сущности:
- сессия открыта, сущность управляемая: выполняется отдельный select по внешнему ключу, коллекция наполняется, следующие обращения идут уже из памяти;
- сессия закрыта, сущность отсоединена:
LazyInitializationException, подгружать некому.
Хорошо назвать штатные решения: подтянуть коллекцию сразу через join fetch или граф сущностей, собирать проекцию в DTO прямо запросом, для пачки владельцев включать пакетную загрузку. Если объект нужен снаружи целиком, коллекцию инициализируют явно, пока транзакция ещё жива.
Где спотыкаются
Лечат исключение переводом связи в жадную загрузку. Симптом уходит, но коллекция начинает тянуться всегда, включая списки и экраны, где она не нужна, а на двух коллекциях сразу запрос ещё и раздувается в декартово произведение.
Не видят N+1. Цикл по владельцам, внутри которого трогают коллекцию, даёт один запрос на список и по одному на каждую строку; на трёх записях в тесте это незаметно, на боевых данных превращается в сотни запросов.
Надеются, что транзакция как-нибудь дотянется до слоя представления. Держать сессию открытой на время отрисовки значит спрятать проблему: запросы начинают уходить из шаблона, и найти их потом по коду почти невозможно.