Используя Hibernate 4.1 и JPA 2.0 с Entity Manager, каков оптимальный способ загрузки нескольких объектов с подобъектами на несколько уровней?
Мы используем Hibernate 4.1.4 с реализацией менеджера сущностей и очень довольны. При загрузке одного объекта или небольших наборов объектов (10-50 + дочерних объектов) мы получаем отличную производительность, и все наши объединения прекрасно работают и являются двунаправленными. Редактирование / сохранение / удаление очень просто!
Загрузка 2500 объектов и связанных данных для отчетности
Мы сталкиваемся с проблемами, когда пытаемся загрузить сотни или тысячи объектов вместе с подобъектами. В одном примере у нас есть около 2500 базовых объектов, а также два соединения "один к одному" и три объединения "один ко многим". Это приводит к 2500 х 5 +1 запросов. Требуется около 30 секунд для выполнения с нашей базой данных (DB2).
Мы используем динамический запрос, а менеджер сущностей создает метод запроса:
http://docs.jboss.org/hibernate/orm/4.1/javadocs/
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html
Попытка уговорить Hibernate использовать Joins
Наши наблюдения заключались в том, что независимо от того, что мы пытаемся, мы не можем получить гибернацию для выполнения загрузок дочерних объектов в качестве соединений в основном запросе. При загрузке одного объекта с использованием метода find и по первичному ключу hibernate использует объединения, но при загрузке нескольких базовых объектов это не так.
Мы попробовали критерии API с тем же результатом.
Некоторый успех извлечения нескольких объектов в запросе
Пример:
select a,b from table1 a left join table2 b
Мы добились частичного успеха, выполняя объединения вручную в нашем запросе и извлекая отдельные сущности с помощью кортежей, но это обходит нашу аннотированную модель сущностей и не является предпочтительным методом (если мы можем избежать этого).
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/
Проблема, с которой мы столкнулись с кортежами, заключается в том, что они отделены от первичной сущности (что кажется логичным, поскольку они не загружаются посредством аннотированных связей), и, кажется, нет никакого способа повторно присоединить их. После добавления вручную всех записей "один ко многим" в список объекта, когда hibernate нажимает на метод get для списка, он все еще пытается загружать список "изо всех сил" (несмотря на то, что список уже установлен).
Попытка оптимизации - предварительная загрузка наборов данных
Другой метод оптимизации, который мы попытались, заключался в предварительной загрузке наборов данных заранее. После загрузки всех основных сущностей мы создали список ПК и запустили один запрос, чтобы загрузить все наборы данных "один ко многим". Но опять же, когда hibernate попадает в получатель для данных списка, он снова пытается загружать / загружать список, игнорируя то, что уже предварительно загружено в сеанс (возможно, нужно включить кэширование?).
Предложения?
Мы идем в разных направлениях с небольшим успехом и надеемся, что кто-то сможет указать нам правильный путь (или указать новый!)!
1 ответ
Кажется, я понял это сам! Две стратегии решили проблему:
Выборка соединения в запросе заботится о соединении один-ко-многим! Конечно, вы можете сделать только один выбор соединения, так как Hibernate сгенерирует исключение одновременной загрузки нескольких сумок.
Пример:
LEFT OUTER JOIN FETCH t.table3 t3
Для дополнительных объединений @Fetch(FetchMode.SUBSELECT) сделал свое дело!
Эффективно сократил общее количество запросов с тысяч до нескольких. В одном примере было выбрано 188 основных объектов, и всего было выполнено 12 запросов. Дополнительные запросы в основном являются поисками один-к-одному и не оказывают существенного влияния на общее время выполнения (в среднем 1-2 секунды). Независимо от того, выбраны ли 188 или 1888 записей, общее количество запросов существенно не увеличивается.
Если кому-то нужна дополнительная информация, свяжитесь со мной или оставьте комментарий!:)