Есть ли способ изменить тип выборки JPA для метода? - PullRequest
18 голосов
/ 24 июня 2009

Есть ли способ изменить тип выборки 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();
}

Как бы я заставил этот метод (или весь класс) использовать усердную выборку?

Ответы [ 4 ]

15 голосов
/ 24 июня 2009

Я предполагаю, что ваши ассоциации сущностей (@OneToOne, @OneToMany, @ManyToOne) являются ленивыми (FetchType.Lazy).

Тогда я могу придумать два пути:

A. напишите два jpa-запроса, один из которых извлекает ассоциацию ленивых (это путь по умолчанию для спящего режима), а второй - явно вызывает принудительную загрузку ассоциации (см. ключевое слово «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);
9 голосов
/ 30 июня 2009

В 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 в качестве аннотации в 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. Я изменил имя поля, чтобы отразить более реалистичный сценарий (по умолчанию только коллекции и 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>
1 голос
/ 10 августа 2017

В 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 для графа сущностей JPA?

1 голос
/ 20 мая 2014

Поскольку никто не упомянул 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

...