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

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


person James McMahon    schedule 24.06.2009    source источник


Ответы (4)


Я предполагаю, что ваши ассоциации сущностей (@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) для принудительной загрузки ленивых отношений объекта после его извлечения (например, через средство поиска...)

ErrorCode lazyCode = findErrorCodeById(1);
// eager load associations
Hibernate.initialize(lazyCode);
person H2000    schedule 24.06.2009
comment
Спасибо, Клаус, очевидно, вариант B сделает код зависимым от импорта Hibernate, что является недостатком, но я попробую. - person James McMahon; 25.06.2009
comment
Привет, Немо, не мог бы ты отметить мой ответ зеленой галочкой. ;) - person H2000; 25.06.2009
comment
Я все еще сталкиваюсь с проблемой закрытия сеанса, используя метод b. - person James McMahon; 30.06.2009
comment
Вы вызывали Hibernate.initialize во время транзакции? Транзакция tx = ноль; Сеанс сеанса = SessionFactoryUtil.getInstance().getCurrentSession(); попробуйте {tx = session.beginTransaction(); ErrorCode lazyCode = findErrorCodeById(1); // готовые ассоциации загрузки Hibernate.initialize(lazyCode); транзакция.коммит(); } ... - person H2000; 30.06.2009
comment
Клаус, на самом деле я использую аннотированные сеансы/транзакции, управляемые Spring. - person James McMahon; 30.06.2009
comment
Возвращаясь к этому вопросу с немного большим опытом работы с JPA, я теперь понимаю, что у JPA есть некоторые серьезные ограничения, по крайней мере, в начальной версии 2, возможно, исправить некоторые из этих проблем. Так что ваше предложение использовать Hibernate напрямую, вероятно, является правильным. - person James McMahon; 21.01.2010

В JPA режим выборки указывается для каждого атрибута сохраняемости либо через аннотацию, либо в файле сопоставления xml.

Таким образом, независимый от поставщика JPA способ достижения вашей цели состоит в том, чтобы иметь отдельный файл сопоставления для каждого уровня DAO. К сожалению, для этого потребуется отдельный PersistenceUnit для каждого файла сопоставления, но вы можете, по крайней мере, совместно использовать одни и те же классы сущностей и один и тот же запрос JPQL.

Далее следуют скелеты кода.

постоянство.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-ленивый.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. Я изменил имя поля, чтобы отразить более реалистичный сценарий (по умолчанию только коллекции и большие двоичные объекты используют 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>
person Mike    schedule 30.06.2009
comment
Майк, не могли бы вы рассказать подробнее о том, что на самом деле вам не нужны два файла сопоставления, вы можете указать либо LAZY, либо EAGER в качестве аннотации в Entity, а затем указать противоположное в файле сопоставления xml? - person James McMahon; 30.06.2009
comment
Я добавил быстрый пример. Надеюсь, поможет. - person Mike; 01.07.2009
comment
Мы отредактируем xml для изменения аннотаций, это очень похоже на редактирование объекта сущности. Однако это может быть лучшим вариантом, который у меня есть. - person James McMahon; 01.07.2009

В 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 ?

person Alfonso Nishikawa    schedule 10.08.2017

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

person zawhtut    schedule 20.05.2014