Предотвратите Dozer от запуска отложенной загрузки Hibernate - PullRequest
16 голосов
/ 05 апреля 2011

Я использую транзакции Spring, поэтому транзакция все еще активна, когда происходит преобразование POJO в DTO.

Я бы хотел, чтобы Dozer не запускал отложенную загрузку, чтобы скрытые sql-запросы никогда не выполнялись: вся выборка должна выполняться явно через HQL (чтобы получить лучший контроль над производительностью).

  1. Это хорошая практика (я нигде не могу ее найти)?

  2. Как это сделать безопасно?

Я пробовал это до преобразования DTO:

PlatformTransactionManager tm = (PlatformTransactionManager) SingletonFactoryProvider.getSingletonFactory().getSingleton("transactionManager");
tm.commit(tm.getTransaction(new DefaultTransactionDefinition()));

Я не знаю, что происходит с транзакцией, но сеанс Hibernate не закрывается, и ленивая загрузка по-прежнему происходит.

Я пробовал это:

SessionFactory sf = (SessionFactory) SingletonFactoryProvider.getSingletonFactory().getSingleton("sessionFactory");
sf.getCurrentSession().clear();
sf.getCurrentSession().close();

И это предотвращает отложенную загрузку, но является ли хорошей практикой манипулирование сеансом непосредственно на уровне приложения (который в моем проекте называется «фасад»)? Какие негативные побочные эффекты я должен бояться? (Я уже видел, что тесты, включающие преобразования POJO -> DTO, больше нельзя запускать с помощью тестовых классов AbstractTransactionnalDatasource Spring, поскольку эти классы пытаются инициировать откат транзакции, которая больше не связана с активным сеансом).

Я также пытался установить распространение на NOT_SUPPORTED или REQUIRES_NEW, но он повторно использует текущий сеанс Hibernate и не предотвращает отложенную загрузку.

Ответы [ 6 ]

24 голосов
/ 11 мая 2011

Единственное общее решение, которое я нашел для управления этим (после просмотра пользовательских конвертеров, прослушивателей событий и прокси-серверов Resolvers), заключается в реализации Custom Field Mapper. Я обнаружил, что эта функциональность скрыта в Dozer API (я не верю, что это задокументировано в Руководстве пользователя).

Простой пример:

public class MyCustomFieldMapper implements CustomFieldMapper 
{
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) 
    {       
        // Check if field is a Hibernate collection proxy
        if (!(sourceFieldValue instanceof AbstractPersistentCollection)) {
            // Allow dozer to map as normal
            return false;
        }

        // Check if field is already initialized
        if (((PersistentSet) sourceFieldValue).wasInitialized()) {
            // Allow dozer to map as normal
            return false;
        }

        // Set destination to null, and tell dozer that the field is mapped
        destination = null;
        return true;
    }   
}

Это вернет все неинициализированные объекты PersistentSet как ноль. Я делаю это так, чтобы, когда они передаются клиенту, я мог различать пустую (не загруженную) коллекцию и пустую коллекцию. Это позволяет мне определить общее поведение в клиенте, чтобы использовать предварительно загруженный набор или сделать другой вызов службы для получения набора (если требуется). Кроме того, если вы решите загружать любые коллекции на уровне сервиса, они будут отображаться как обычно.

Я добавляю специальный маппер поля, используя пружину:

<bean id="dozerMapper" class="org.dozer.DozerBeanMapper" lazy-init="false">
    <property name="mappingFiles">
        ...
    </property>
    <property name="customFieldMapper" ref="dozerCustomFieldMapper" />
</bean>
<bean id="dozerCustomFieldMapper" class="my.project.MyCustomFieldMapper" />

Надеюсь, это поможет любому, кто ищет решение для этого, поскольку я не смог найти никаких примеров при поиске в Интернете.

10 голосов
/ 11 января 2013

Вариант популярной версии, приведенной выше, гарантирует, что вы поймаете оба PersistentBags, PersistentSets, вы называете это ...

public class LazyLoadSensitiveMapper implements CustomFieldMapper {

public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) {
    //if field is initialized, Dozer will continue mapping

    // Check if field is derived from Persistent Collection
    if (!(sourceFieldValue instanceof AbstractPersistentCollection)) {
        // Allow dozer to map as normal
        return false;
    }

    // Check if field is already initialized
    if (((AbstractPersistentCollection) sourceFieldValue).wasInitialized()) {
        // Allow dozer to map as normal
        return false;
    }

    return true;
}

}

5 голосов
/ 23 сентября 2011

Я не получил вышеуказанное для работы (возможно, разные версии).Однако это прекрасно работает

public class HibernateInitializedFieldMapper implements CustomFieldMapper {
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) {
        //if field is initialized, Dozer will continue mapping
        return !Hibernate.isInitialized(sourceFieldValue));
    }
}
3 голосов
/ 05 апреля 2011

Рассматривали ли вы вообще отключение отложенной загрузки?

Похоже, что это не совсем соответствует шаблонам, которые вы хотели бы использовать:

Я бы хотел, чтобы Dozer не запускал отложенную загрузку, чтобы скрытые запросы sqlникогда не происходит: вся выборка должна выполняться явно через HQL (чтобы получить лучший контроль над производительностью).

Это говорит о том, что вы никогда не захотите использовать отложенную загрузку.

Dozer ибобы с гибернацией, которые вы передаете ему, блаженно не знают друг друга;Все, что знает Dozer, - это то, что он обращается к свойствам в bean-компоненте, а bean-компонент с поддержкой Hibernate отвечает на вызовы get() lazy-нагруженной коллекции, как если бы вы обращались к этим свойствам самостоятельно.

Любые уловки, чтобы Dozer знал о прокси Hibernate в ваших компонентах или наоборот, IMO, сломали бы слои вашего приложения.

Если вам не нужен «скрытый SQL»запросы "запускаются в неожиданное время, просто отключите ленивую загрузку.

0 голосов
/ 03 ноября 2014

Использование CustomFieldMapper может не быть хорошей идеей, поскольку оно будет вызываться для каждого поля вашего исходного класса, но наша задача - только ленивое сопоставление ассоциации (список дочерних объектов), поэтому мы можем установить нулевое значение в получателе объекта сущности

1002 *
0 голосов
/ 22 марта 2013

Короткая версия этого картографа будет

return sourceFieldValue instanceof AbstractPersistentCollection && 
!( (AbstractPersistentCollection) sourceFieldValue ).wasInitialized();
...