Hibernate (JPA), как сделать активный запрос, загружая все дочерние объекты - PullRequest
20 голосов
/ 16 сентября 2008

Относительно моего более раннего вопроса , я хочу убедиться, что все дочерние объекты загружены, так как у меня есть несколько потоков, которые могут понадобиться для доступа к данным (и, таким образом, избежать исключений при отложенной загрузке). Я понимаю, что способ сделать это - использовать ключевое слово «fetch» ​​в запросе (EJB QL). Как это:

select distinct o from Order o left join fetch o.orderLines

Предполагается, что модель с классом Order имеет набор OrderLines.

Мой вопрос заключается в том, что «отдельное» ключевое слово, по-видимому, необходимо, так как в противном случае я получаю Order для каждого OrderLine. Я правильно поступаю?

Возможно, более важно, есть ли способ втянуть все дочерние объекты, независимо от их глубины? У нас около 10-15 классов, и для сервера нам понадобится все загруженное ... Я избегал использования FetchType.EAGER, поскольку это означало, что он всегда готов и, в частности, веб-интерфейс загружает все - но, возможно, это путь - это то, что вы делаете? Кажется, я помню, как мы пытались делать это раньше, а затем получали действительно медленные веб-страницы, но, возможно, это означает, что нам следует использовать кэш второго уровня?

Ответы [ 7 ]

14 голосов
/ 16 сентября 2008

Изменение аннотации - плохая идея IMO. Поскольку это не может быть изменено на ленивое во время выполнения. Лучше сделать все ленивым и доставать по мере необходимости.

Я не уверен, что понимаю вашу проблему без сопоставлений. Левое соединение должно быть всем, что вам нужно для описанного вами варианта использования. Конечно, вы вернете заказ для каждой строки заказа, если строка заказа имеет своего родителя.

8 голосов
/ 16 сентября 2008

Я не уверен насчет использования ключевого слова fetch в вашем EJBQL, вы можете спутать его с аннотацией ...

Вы пытались добавить свойство FetchType в свой атрибут отношений?

@ OneToMany (скачивает = FetchType.EAGER)

См .:

http://java.sun.com/javaee/5/docs/api/javax/persistence/FetchType.html http://www.jroller.com/eyallupu/entry/hibernate_exception_simultaneously_fetch_multiple

3 голосов
/ 23 октября 2008

Вы пробовали использовать преобразователь результатов? Если вы используете запросы Criteria, вы можете применить преобразователь результатов (хотя есть некоторые проблемы с разбиением на страницы и преобразователем результатов ):

Criteria c = ((Session)em.getDelegate()).createCriteria(Order.class);
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
c.list();

em.getDelegate() - это хак, который работает, только если вы используете спящий режим.

Возможно, более важно, есть ли способ втянуть все дочерние объекты, нет важно как глубоко? У нас около 10-15 классы и для сервера мы будем нужно все загрузить ... я был избегая использования FetchType.EAGER как такового имел в виду его всегда готовы и в конкретные веб-интерфейсные нагрузки все - но, возможно, это путь - это то, что вы делаете? я кажется, помните, что мы пытались это раньше а затем получить очень медленные веб-страницы - но, возможно, это означает, что мы должны использовать кэш второго уровня?

Если вам все еще интересно, я ответил на аналогичный вопрос в этой теме как сериализовать коллекции hibernate .

Обычно вы используете утилиту под названием dozer , которая отображает бины на другие бины, и тем самым вы запускаете все ваши ленивые загрузки. Как вы можете себе представить, это работает лучше, если все коллекции извлекаются с нетерпением.

2 голосов
/ 16 сентября 2008

Вы можете сделать что-то подобное, используя (отдельный) критерий запроса и установив режим выборки. Например.,

Session s = ((HibernateEntityManager) em).getSession().getSessionFactory().openSession();
DetachedCriteria dc = DetachedCriteria.forClass(MyEntity.class).add(Expression.idEq(id));
dc.setFetchMode("innerTable", FetchMode.JOIN);
Criteria c = dc.getExecutableCriteria(s);
MyEntity a = (MyEntity)c.uniqueResult();
0 голосов
/ 16 сентября 2008

Если проблема заключается только в LazyInitializationExceptions, вы можете избежать этого, добавив OpenSessionInViewFilter.
Это позволит загружать объекты в виде, но не поможет с проблемой скорости.

     <filter>
        <filter-name>hibernateFilter</filter-name>
        <filter-class> org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hibernateFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
0 голосов
/ 16 сентября 2008

Я выполнил рефакторинг кода, чтобы сохранить карту объектов для менеджеров сущностей, и каждый раз, когда мне нужно обновить, закройте старый диспетчер сущностей для объекта и откройте новый. Я использовал вышеупомянутый запрос без fetch , так как он слишком глубокий для моих нужд - просто выполнение простого объединения в OrderLines - fetch делает его еще глубже.

Есть только несколько объектов, для которых мне нужно это, около 20, так что я думаю, что нехватка ресурсов при наличии 20 открытых менеджеров сущностей не является проблемой - хотя администраторы баз данных могут иметь другое представление, когда они будут запущены ...

Я также переработал вещи так, чтобы работа с БД осуществлялась в главном потоке и имела менеджер сущностей.

Chris

0 голосов
/ 16 сентября 2008

Это будет работать только для отношений ManyToOne и для них, вероятно, подойдет @ManyToOne (fetch = FetchType.EAGER).

Получение более чем одного отношения OneToMany охотно не поощряется и / или не работает, как вы можете прочитать по ссылке, опубликованной Джереми. Подумайте только об операторе SQL, который понадобился бы для такой выборки ...

...