Есть ли способ изменить тип выборки JPA в методе?
-
10-07-2019 - |
Вопрос
Есть ли способ изменить тип выборки JPA в одном методе без редактирования объекта entity?
У меня есть общий уровень ORM, состоящий из классов сущностей JPA.Доступ к этому слою ORM осуществляется двумя слоями DAO.Одному DAO нужна отложенная выборка, как для моего веб-приложения, другому нужна быстрая выборка, так как мне нужно, чтобы оно было потокобезопасным.
Вот пример метода из моего threadsafe 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();
}
Как бы я заставил этот метод (или весь класс) использовать нетерпеливую выборку?
Решение
Я предполагаю, что ваши ассоциации сущностей (@OneToOne, @OneToMany, @ManyToOne) признаны ленивыми (FetchType.Lazy)
Тогда я могу придумать два способа:
A.напишите два jpa-запроса, один из которых извлекает ассоциацию lazy (это способ по умолчанию для hibernate), и второй запрос, который явно принудительно загружает ассоциацию (см. Ключевое слово "fetch" в запросе).
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");
B.используйте Hibernate.initialize(entity), чтобы принудительно загрузить отложенные отношения объекта после его извлечения (напримерчерез finder ...)
ErrorCode lazyCode = findErrorCodeById(1); // eager load associations Hibernate.initialize(lazyCode);
Другие советы
В JPA режим извлечения указывается для каждого атрибута постоянства, либо с помощью аннотации, либо в файле сопоставления 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 в качестве аннотации в сущности, а затем указать противоположное в файле сопоставления 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. Я изменил имя поля, чтобы отразить более реалистичный сценарий (по умолчанию только коллекции и BLOB-объекты используют 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>
Поскольку никто не упомянул 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 р>
В JPA2 я использую EntityGraphs , который позволяет вам определить, какие связанные объекты вы хотите получить:
https://docs.oracle.com/javaee/7 /tutorial/persistence-entitygraphs002.htm https://docs.oracle.com/javaee/7/tutorial/ сохранение-entitygraphs003.htm р>
Вы создаете NamedQuery, как вы это сделали, и вы прикрепляете подсказку с ключом javax.persistence.loadgraph
или javax.persistence.fetchgraph
. Он извлечет связанные сущности, которые вы определили на графике.
Вы можете найти подробную информацию о разнице между " loadgraph " и " fetchgraph " здесь: В чем разница между FETCH и LOAD для графа сущностей JPA?