Есть ли способ изменить тип выборки JPA для метода?
Есть ли способ изменить тип выборки JPA для одного метода без редактирования объекта сущности?
У меня есть общий слой ORM, состоящий из классов сущностей JPA. Доступ к этому уровню ORM осуществляется двумя уровнями DAO. Один DAO нуждается в отложенной загрузке, как и для моего веб-приложения, а другой - в стремительной загрузке, так как он нужен для обеспечения безопасности потоков.
Вот пример метода из моего многопоточного DAO,
@PersistenceContext(unitName = "PersistenceUnit", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
public ErrorCode findErrorCodeById(short id) {
return (ErrorCode) em.createNamedQuery("ErrorCode.findById").
setParameter("id", id).getSingleResult();
}
Как бы я заставил этот метод (или весь класс) использовать нетерпеливую выборку?
Ответы
Ответ 1
Я предполагаю, что ваши ассоциации сущностей (@OneToOne, @OneToMany, @ManyToOne) fechted lazy (FetchType.Lazy)
Тогда я могу думать о двух путях:
а. напишите два запроса jpa, которые получают ассоциацию lazy (это путь по умолчанию для hibernate) и второй запрос, который явно принудительно загружает ассоциацию (см. ключевое слово "выборка" в запросе).
Query q = HibernateUtil.getSessionFactory().getCurrentSession()
.createQuery("select c from Category as c" +
" left join fetch c.categorizedItems as ci" +
" join fetch ci.item as i");
В. используйте Hibernate.initialize(entity), чтобы принудительно загружать ленивые отношения объекта после того, как вы его извлекли (например, через finder...)
ErrorCode lazyCode = findErrorCodeById(1);
// eager load associations
Hibernate.initialize(lazyCode);
Ответ 2
В JPA режим Fetch указывается для каждого атрибута персистентности либо через аннотацию, либо в файле сопоставления xml.
Таким образом, агентистский способ поставщика JPA для достижения вашей цели состоит в том, чтобы иметь отдельный файл сопоставления для каждого слоя DAO. К сожалению, для каждого файла сопоставления потребуется отдельный PersistenceUnit, но вы можете, по крайней мере, использовать одни и те же классы сущностей и один и тот же запрос JPQL.
Следуют кодовые скелеты.
persistence.xml:
<persistence>
<persistence-unit name="dao-eager">
<mapping-file>orm-eager.xml</mapping-file>
</persistence-unit>
<persistence-unit name="dao-lazy">
<mapping-file>orm-lazy.xml</mapping-file>
</persistence-unit>
</persistence>
orm-eager.xml:
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<basic name="name" fetch="EAGER"/>
</attributes>
</entity>
</entity-mappings>
orm-lazy.xml:
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<basic name="name" fetch="LAZY"/>
</attributes>
</entity>
</entity-mappings>
Тогда это просто вопрос создания EntityManagerFactory для соответствующей единицы персистентности в ваших слоях DAO.
На самом деле вам не нужны два файла сопоставления, вы можете указать LAZY или EAGER как аннотацию в Entity, а затем указать противоположное в файле сопоставления xml (вам все равно потребуется два модуля сохранения).
Может быть немного больше кода, чем решение Hibernate выше, но ваше приложение должно быть переносимым для других поставщиков JPA.
В стороне OpenJPA обеспечивает аналогичную функциональность для решения Hibernate выше, используя FetchGroups (концепция, заимствованная из JDO).
Последнее предостережение, FetchType.LAZY - это подсказка в JPA, поставщик может загружать строки с нетерпением, если это необходимо.
Обновлено для каждого запроса.
Рассмотрим объект вроде этого:
@Entity
public class ErrorCode {
// . . .
@OneToMany(fetch=FetchType.EAGER) // default fetch is LAZY for Collections
private Collection myCollection;
// . . .
}
В этом случае вам все равно понадобятся две единицы сохранения, но вам потребуется только orm-lazy.xml. Я изменил название поля, чтобы отразить более реалистичный сценарий (только коллекции и blobs используют FetchType.LAZY по умолчанию). Таким образом, полученный файл orm-lazy.xml может выглядеть так:
<entity-mappings>
<entity class="ErrorCode">
<attributes>
<one-to-many name="myCollection" fetch="LAZY"/>
</attributes>
</entity>
</entity-mappings>
И persistence.xml будет выглядеть так:
<persistence>
<persistence-unit name="dao-eager">
<!--
. . .
-->
</persistence-unit>
<persistence-unit name="dao-lazy">
<!--
. . .
-->
<mapping-file>orm-lazy.xml</mapping-file>
</persistence-unit>
</persistence>
Ответ 3
Поскольку никто не упоминает OpenJPA, я отвечу здесь.
В OpenJPA предыдущие ленивые настроенные коллекции и поля могут быть загружены как ниже
OpenJPAEntityManager kem = OpenJPAPersistence.cast(em);
kem.getFetchPlan().addField(Order.class, "products");
TypedQuery<Order> query = kem.createQuery(yourQuery, Order.class);
Ссылка: http://openjpa.apache.org/builds/1.0.3/apache-openjpa-1.0.3/docs/manual/ref_guide_fetch.html
Ответ 4
В JPA2 я использую EntityGraphs, который позволяет вам определить, какие связанные объекты вы хотите получить:
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs002.htm
https://docs.oracle.com/javaee/7/tutorial/persistence-entitygraphs003.htm
Вы создаете NamedQuery, как и вы, и вы прикрепляете подсказку с ключом javax.persistence.loadgraph
или javax.persistence.fetchgraph
. Он будет извлекать связанные объекты, которые вы определили в графе.
Здесь вы можете найти подробности разницы между "loadgraph" и "fetchgraph": В чем разница между FETCH и LOAD для диаграммы Entity JPA?