2016-12-21 2 views
0

Обычно я работаю над трехуровневыми приложениями, использующими Hibernate в уровне персистентности, и я стараюсь не использовать классы модели домена в уровне презентации. Вот почему я использую шаблон дизайна DTO (Data Transfer Object).Сохранение Hibernate Lazy loading в шаблоне проектирования объектов передачи данных

Но у меня всегда есть дилемма в моем объектно-ориентированном сопоставлении. Я теряю ленивую загрузку, или создаю сложность кода, вводя фильтры для вызова или не модели домена getters.


Пример:Рассмотрим DTO UserDto, что соответствует сущности пользователя

public UserDto toDto(User entity, OptionList... optionList) { 

     if (entity == null) { 
      return null; 
     } 

     UserDto userDto = new UserDto(); 
     userDto.setId(entity.getId()); 
     userDto.setFirstname(entity.getFirstname()); 


     if (optionList.length == 0 || optionList[0].contains(User.class, UserOptionList.AUTHORIZATION)) { 
      IGenericEntityDtoConverter<Authorization, AuthorizationDto> authorizationConverter = converterRegistry.getConverter(Authorization.class); 

      List<AuthorizationDto> authorizations = new ArrayList<>(authorizationConverter.toListDto(entity.getAuthorizations(), optionList)); 
      userDto.setAuthorizations(authorizations); 

... 
} 

OptionList используется для фильтрации отображения и отображения только что хотел.

Хотя последнее решение допускает ленивую загрузку, но оно очень тяжелое, поскольку в сервисном слое должен быть указан optionList.


Есть ли лучшее решение для сохранения отложенной загрузки в шаблоне проектирования DTO?

+0

Как это работает? В большинстве случаев, когда вы используете DTO/3-ярус, транзакция закрывается, когда вы выходите из уровня обслуживания. Как ваша ленивая загрузка при работе? Если вы используете шаблон открытого сеанса в представлении, почему бы не использовать напрямую спящие объекты, которые уже реализуют ленивую загрузку? Вы можете иметь несколько объектов для одной и той же таблицы, использовать специализированный для пользовательского интерфейса, который вы устанавливаете в режиме только для чтения при загрузке. – Thierry

+0

Спасибо за ваш ответ Тьерри. Фактически ленивая загрузка выполняется в сервисном слое. Уровень презентации просто сообщает сервисному слою, что он должен быть восстановлен с помощью 'optionList', который затем используется для вызова или отсутствия получателя модели домена. Таким образом, это предотвращает вызов базы данных, если это не обязательно. Пример службы:. '@Transactional \t общественного UserDto FindByName (имя String, окончательное OptionList ... optionList) { \t \t возвращение GetConverter() toDto (dao.findByName (имя), optionList); \t} ' –

+0

ОК, я понимаю ваш вопрос лучше сейчас – Thierry

ответ

1

Для одного и того же постоянного состояния я не люблю иметь поля объекта, не инициализированные в некотором пути выполнения, в то время как эти поля также могут быть инициализированы в других случаях. Эта причина слишком много головной боли для поддержания:

  • это вызовет NullPointer в лучших случаях
  • если null также является допустимым вариантом (и, следовательно, не вызывают NullPointer), это может означать, что данные были удалены, и может вызвать неожиданные бизнес-правила удаления, в то время как данные на самом деле все еще существуют.

Я предпочел бы создать иерархию DTO интерфейсов и/или классов, начиная с UserDto. Все фактические поля реализации dto заполняются, чтобы отразить постоянное состояние: если есть данные, поле dto не равно null.

Так то вам просто необходимо обратиться к сервис-слой, реализация DTO вы хотите:

public <T extends UserDto> T toDto(User entity, Class<T> dtoClass) { 
    ... 
} 

Затем в слое службы, вы можете иметь:

Map<Class<? extends UserDto>, UserDtoBUilder> userDtoBuilders = ... 

где вы регистрируетесь различные разработчики, которые будут создавать и инициализировать различные реализации UserDto.

0

Я не уверен, зачем вам нужна ленивая загрузка, но я думаю, потому что ваш UserDto обслуживает несколько изображений через конфигурацию optionList? Я не знаю, как выглядит ваш код уровня презентации, но, наверное, у вас есть код if-else для каждого элемента в optionList?

Как насчет того, чтобы иметь разные представления, т. Е. Подклассы? Я спрашиваю об этом, потому что я хотел бы предложить попробовать Blaze-Persistence Entity Views. Вот небольшой пример кода, который подходит вашему домену.

@EntityView(User.class) 
public interface SimpleUserView { 
    // The id of the user entity 
    @IdMapping("id") int getId(); 

    String getFirstname(); 
} 

@EntityView(Authorization.class) 
public interface AuthorizationView { 

    // The id of the authorization entity 
    @IdMapping("id") int getId(); 

    // Whatever properties you want 
} 

@EntityView(User.class) 
public interface AuthorizationUserView extends SimpleUserView { 

    List<AuthorizationView> getAuthorizations(); 
} 

Это DTOS с некоторыми метаданными о отображении на модель объекта. И тут приходит использование:

@Transactional 
public <T> T findByName(String name, EntityViewSetting<T, CriteriaBuilder<T>> setting) { 
    // Omitted DAO part for brevity 

    EntityManager entityManager = // jpa entity manager 
    CriteriaBuilderFactory cbf = // query builder factory from Blaze-Persistence 
    EntityViewManager evm = // manager that can apply entity views to query builders 

    CriteriaBuilder<User> builder = cbf.create(entityManager, User.class) 
     .where("name").eq(name); 
    List<T> result = evm.applySetting(builder, setting) 
     .getResultList(); 
    return result; 
} 

Теперь, если вы используете его как service.findByName("someName", EntityViewSetting.create(SimpleUserView.class)) он будет генерировать запрос как

SELECT u.id, u.firstname 
FROM User u 
WHERE u.name = :param_1 

и если вы используете другой вид, как service.findByName("someName", EntityViewSetting.create(AuthorizationUserView.class)) он будет генерировать

SELECT u.id, u.firstname, a.id 
FROM User u LEFT JOIN u.authorizations a 
WHERE u.name = :param_1 

Помимо возможности избавиться от сопоставления объектов вручную, производительность улучшится из-за использования оптимизированных запросов!

+0

Привет, христианин, спасибо за ваш ответ. Действительно, ваше решение повышает производительность. Я хочу знать, как вы справляетесь с вашим evm.applySetting и что на самом деле делает? –

+0

Он применяет проекции из класса представления сущности к построителю построителя запросов и регистрирует на нем так называемый «ObjectBuilder». Этот объект-строитель берет кортежи из результата запроса и строит объекты типа вашего сущностного вида. –