Комментарии 35
entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation();
если я правильно все понимаю, то получается что на каждый элемент списка генерируется запрос в базу, что не есть хорошо
0
Если я не ошибаюсь, то при автоматической инициализации объектов Hibernate'ом для каждого объекта из списка составляется свой запрос.
В конкретно этом случае — мы можем запросить и целый список объектов отдельным запросом. Но не факт, что мы опять не получим прокси-объекты.
В конкретно этом случае — мы можем запросить и целый список объектов отдельным запросом. Но не факт, что мы опять не получим прокси-объекты.
0
Ну вобщем то Hibernate создает прокси список который не содержит объектов, а потом уже идет select по FK и создается список при вызове геттера.
Мне кажется такое вот решение будет дико тормозить на большом количестве объектов.
Мне кажется такое вот решение будет дико тормозить на большом количестве объектов.
0
На счет прокси-списка все верно. При запросе списка геттером Hibernate заполняет его объектами. Но некоторые из этих объектов могут быть не инициализированными. И при вызове instanceof для одного из них, мы можем столкнуться с proxy-объектом.
Только что проверил создаваемые hibernate'ом запросы при вызове геттера. Выбор идет по PK каждого объекта из ленивого списка. На каждый объект — отдельный запрос.
Для проверки выставлял следующие уровни логгирования:
Только что проверил создаваемые hibernate'ом запросы при вызове геттера. Выбор идет по PK каждого объекта из ленивого списка. На каждый объект — отдельный запрос.
Для проверки выставлял следующие уровни логгирования:
- org.hibernate.SQL=DEBUG
- org.hibernate.type.descriptor.sql.BasicBinder=TRACE
0
а откуда же он взял эти PK, если при FetchType.LAZY не генерятся join'ы? у вас какая то магия выходит
0
Точно странно. У меня в логах hibernate идет запрос «User», вызываю геттер — появляются запросы телефонов по их PK. А где он эти PK взял не ясно. Отдельного запроса списка PK не было.
Займусь вечером изучением этого вопроса.
Займусь вечером изучением этого вопроса.
0
Разобрался в этой теме более подробно. Проверял на рабочем коде, отключил весь описанный в статье код. Алгоритм работы следующий:
- вызываем геттер у «User» списка «Phone»
- Hibernate выполняет запрос, который включает в себя все поля столбцы базового класса, join'ит всех потомков и выбирает у них по 2 разных столбца, дополнительно в запросе идет проверка к какому классу принадлежит строка
- для каждого объекта из списка вызывает запрос с его уже известным PK, к его таблице. В запросе собираются недостающие столбцы объекта
0
И да, использовать LinkedList для временного хранения нынче круто?
+1
Проблема, конечно, интересная. Решение, конечно, некрасивое. Но меня терзают смутные сомнения, что тип телефона необходимо определять через instanceof. Вообще, логика, построенная на instanceof, попахивает.
+1
Кроме того, насколько я помню, instanceof довольно медленная операция.
0
Ситуации бывают разные. Когда функционал должен быть готов вчера особо не задумываешься по поводу рефакторинга.
0
По крайней мере, можно заменить на проверку по
getClass()
, если не надо подклассы чекать.0
А ещё в Hibernate есть такой режим ленивых ассоциаций, как no-proxy (включается аннотацией
@LazyOtOne(LazyToOneOption.NO_PROXY)
. Правда, для их правильной работы требуется включить bytecode instrumentation, но в этом нет ничего страшного. Ещё один недостаток — требуется всюду не забывать указывать эту Hibernate-специфическую аннотацию, но даже этот недостаток можно преодолеть.+1
Пробовали запустить на хотя бы пару тысячах элементов? Тормозить будет жууутко. Вообще, в местах где нужно делать подобного рода проверки, лучше делать join'ы сразу.
Hibernate.initialize в цикле это плохо. Замените хотя бы на инициализацию сразу целого списка (вернее Hibernate умеет делать fetch не более «batch size» элементов)
Hibernate.initialize в цикле это плохо. Замените хотя бы на инициализацию сразу целого списка (вернее Hibernate умеет делать fetch не более «batch size» элементов)
0
а при чем здесь batch? O_o
Наверное нужно было сказать 'fetch size'
Наверное нужно было сказать 'fetch size'
0
Нет, именно hibernate.jdbc.batch_size :)
0
batch_size никак не используется для select'ов. Он используется для операций требующих executeUpdate
0
песня совершенно о другом
0
и о чем же?
0
Хорошо, не hibernate.jdbc.batch_size, а просто batch-size. Суть та же
0
Вы сами то ссылку читали?
Там написано, что если есть какие то объекты в сессии у которых есть Lazy поля, то при выборке этого поля для какого нибудь объекта, могут быть заодно выбрана такие же поля для других объектов в сессии.
Вобщем полное непонимание вопроса выходит у вас…
Там написано, что если есть какие то объекты в сессии у которых есть Lazy поля, то при выборке этого поля для какого нибудь объекта, могут быть заодно выбрана такие же поля для других объектов в сессии.
Вобщем полное непонимание вопроса выходит у вас…
0
Вы попробуйте сначала это в практике, а потом говорите, что у меня «непонимание вопроса»
0
Может так понятнее будет: www.mkyong.com/hibernate/hibernate-fetching-strategies-examples/
0
В большинстве случаев, у нас объектов в таких списках меньше сотни.
В случаях когда объектов намного больше, у нас используются запросы по частям (например, по 100 объектов) и следующие части запрашиваются и результаты отправляются клиентам только по требованию.
Отвечая на ваш вопрос: нет, не проверяли.
В случаях когда объектов намного больше, у нас используются запросы по частям (например, по 100 объектов) и следующие части запрашиваются и результаты отправляются клиентам только по требованию.
Отвечая на ваш вопрос: нет, не проверяли.
0
Использую Eclipselink вместо Hibernate, он проксирует только на уровне списков. Но в целом проблемы те же: что делать с lazy объектами вне сессии, и как избавиться от N+1 запросов. Connected-архитектура и lazy инициализация — это огромный антипаттерн, который лимитирует возможность использования объектов только внутри сессии, постоянно напрягая БД огромным количеством тупых запросов. И до сих пор создатели JPA не предусмотрели хорошего способа для обхода ситуации — видимо те, кто пишут JSR, ориентируются на сферического коня в вакууме.
Мы делаем так: при вызове сервиса сначала вытаскивается все, что нужно и только то, что нужно одним или несколькими запросами. Для избавления N+1 запросов можно использовать join fetch или batch-fetch. В JPA 2.1 добавили EntityGraphs позволяющие более просто указывать relations, которые надо вытащить при запросе. Плюс есть нестандартные load-groups и fetch-groups. Если поле lazy и не проинициализировано, оно не должно использоваться. Затем service interceptor прогоняет граф через специальный фильтр, который пробегает все поля, обнуляя непроинициализированные прокси и заменяя проинициализированные коллекции на ArrayList и LinkedHashMap. На выходе получается полностью портабельный detached граф объектов. Почему EntityManager.detach() не делает то же самое — для меня загадка.
Мы делаем так: при вызове сервиса сначала вытаскивается все, что нужно и только то, что нужно одним или несколькими запросами. Для избавления N+1 запросов можно использовать join fetch или batch-fetch. В JPA 2.1 добавили EntityGraphs позволяющие более просто указывать relations, которые надо вытащить при запросе. Плюс есть нестандартные load-groups и fetch-groups. Если поле lazy и не проинициализировано, оно не должно использоваться. Затем service interceptor прогоняет граф через специальный фильтр, который пробегает все поля, обнуляя непроинициализированные прокси и заменяя проинициализированные коллекции на ArrayList и LinkedHashMap. На выходе получается полностью портабельный detached граф объектов. Почему EntityManager.detach() не делает то же самое — для меня загадка.
0
Всё-таки, на какую глубину графа надо доставать объекты, ORM-движку трудно судить. В вашей конкретной ситуации, это может быть и элементарно, но в общем, не так уж и очевидно. Видимо поэтому, разработчики ОРМ отдают это на откуп разработчикам приложений. А те уж используют DTO и все такое.
0
> Видимо поэтому, разработчики ОРМ отдают это на откуп разработчикам приложений.
В том-то и дело, что не отдают. В JPA нет хорошей возможности сказать что конкретно и как доставать. До JPA 2.1 не было даже стандартного способа указать, какие атрибуты мне нужны, а какие нет. Были vendor-specific query hints, и работали через пень-колоду. А основная N+1 проблема до сих пор не имеет решения: JOIN FETCH присоединяет только одну коллекцию. В EclipseLink есть batch hint, который внезапно не работает для ManyToOne и OneToOne.
Видимо, разработчики JPA надеялись, что у нас будет один большой кеш, где будет лежать 80% данных всей базы, с объектами которого будет работать приложение, потихоньку подгружая недостающие части. Однако, как показывает практика, любая страничка с простой таблицей начисто рушит данный подход.
В том-то и дело, что не отдают. В JPA нет хорошей возможности сказать что конкретно и как доставать. До JPA 2.1 не было даже стандартного способа указать, какие атрибуты мне нужны, а какие нет. Были vendor-specific query hints, и работали через пень-колоду. А основная N+1 проблема до сих пор не имеет решения: JOIN FETCH присоединяет только одну коллекцию. В EclipseLink есть batch hint, который внезапно не работает для ManyToOne и OneToOne.
Видимо, разработчики JPA надеялись, что у нас будет один большой кеш, где будет лежать 80% данных всей базы, с объектами которого будет работать приложение, потихоньку подгружая недостающие части. Однако, как показывает практика, любая страничка с простой таблицей начисто рушит данный подход.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Hibernate: ленивая загрузка, наследование и instanceof