Отображение Dozer для объекта Hibernate в DTO

Я пытаюсь использовать Dozer для преобразования объекта моего домена в объекты DTO. Итак, я хочу преобразовать PersistentList, PersistentBag, ... из объекта моего домена в ArrayList, ... в моих объектах DTO, чтобы избежать ленивой проблемы.

Это пример двух моих доменных сущностей:

public class User {
      private Collection<Role> roles;
      ...
}

public class Role {
      private Collection<User> users;
      ...
}

Мои объекты DTO такие же, за исключением того, что класс относится к типам DTO. Итак, чтобы преобразовать домен в объекты DTO, я использую следующее отображение Dozer:

 <configuration>
  <custom-converters>
   <converter type=com.app.mapper.converter.BagConverter">
    <class-a>org.hibernate.collection.PersistentBag</class-a>
    <class-b>java.util.List</class-b>
   </converter>
  </custom-converters>
 </configuration>

 <mapping>
  <class-a>com.app.domain.User</class-a>
  <class-b>com.app.dto.UserDTO</class-b>
 </mapping>

 <mapping>
  <class-a>com.app.domain.Role</class-a>
  <class-b>com.app.dto.RoleDTO</class-b>
 </mapping>

BagConverter - это настраиваемый конвертер Dozer, и это его код:

  public class BagConverter<T> extends DozerConverter<PersistentBag, List>{

 public BagConverter() {
  super(PersistentBag.class, List.class);
 }

 public PersistentBag convertFrom(List source, PersistentBag destination) {
  PersistentBag listDest = null;

  if (source != null) {

   if (destination == null) {
    listDest = new PersistentBag();
   } else {
    listDest = destination;
   }

   listDest.addAll(source);
  }

  return listDest;
 }

 public List convertTo(PersistentBag source, List destination) {
  List listDest = null;

  if (source != null) {

   if (destination == null) {
    listDest = new ArrayList<T>();
   } else {
    listDest = destination;
   }

   if (source.wasInitialized()) {
    listDest.addAll(source);
   }
  }

  return listDest;
 }}

Итак, я получаю объект User, который содержит PersistentBag с ролями. Я применяю карту Dozer Mapper к этому объекту, чтобы получить объект UserDTO. В результате я получаю объект UserDTO с ArrayList для Role, а не с ArrayList для RoleDTO, как я хотел.

Я думал, что даже если я воспользуюсь кастомным конвертером, dozer конвертирует содержимое моего списка. Это неправильный путь? Если нет, как сделать, чтобы преобразовать объект моего домена в объект dto, заменив постоянные коллекции классическими коллекциями Java?

Спасибо за вашу помощь.

Сильвен.


person sylsau    schedule 13.05.2010    source источник
comment
С пометкой "java" вместо "домен". Надеюсь, вы не против, но я верю, что это поможет вам получить более точные ответы.   -  person Grzegorz Oledzki    schedule 13.05.2010


Ответы (4)


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

Как я вижу сейчас (раньше я этого не видел, должно быть, это какая-то новая функция). Существует возможность использовать MapperAware интерфейс, как описано в конце главы для преобразователей пользовательских типов в документации Dozer < / а>. Я думаю, это именно то, что вам нужно.

person Grzegorz Oledzki    schedule 13.05.2010

Я пытаюсь использовать Dozer для преобразования объекта моего домена в объекты DTO. Итак, я хочу преобразовать PersistentList, PersistentBag, ... из объекта моего домена в ArrayList, ... в моих объектах DTO, чтобы избежать ленивой проблемы.

Я получил последнее предложение, но не понимаю, почему вам нужно иметь дело с _ 1_ (и так далее), поскольку этот класс является List. Просто используйте что-то вроде этого:

<mapping>
  <class-a>com.myapp.domain.User</class-a>
  <class-b>com.myapp.dto.UserDTO</class-b>
  <field>
    <a>roles</a>
    <b>roles</b>
    <a-hint>com.myapp.domain.Role</a-hint>
    <b-hint>com.myapp.dto.RoleDTO</b-hint>
  </field>
</mapping>

И выполните преобразование, прежде чем отсоединять объекты (это ключ вашей проблемы).

person Pascal Thivent    schedule 13.05.2010
comment
Я предполагаю, что причина в том, что OP использует некоторые методы, не объявленные в интерфейсе List, например. wasInitialized(). - person Grzegorz Oledzki; 14.05.2010
comment
Моя основная цель - передать объект DTO на уровень представления, который находится в GWT, поэтому коллекции гибернации не поддерживаются для сериализации GWT. Итак, я попытался преобразовать коллекции спящего режима в классические коллекции Java, чтобы избежать этой проблемы. - person sylsau; 14.05.2010
comment
@sylsau Да, я понял. Но я не понимаю, как работа с PersistentBag что-то решает. Вам просто нужно выполнить преобразование, когда Entities все еще прикреплены. - person Pascal Thivent; 14.05.2010

Я также протестировал другое решение без специального конвертера:

<mapping>
    <class-a>com.myapp.domain.User</class-a>
    <class-b>com.myapp.dto.UserDTO</class-b>
    <field>
        <a>roles</a>
        <b>roles</b>
        <a-hint>com.myapp.domain.Role</a-hint>
        <b-hint>com.myapp.dto.RoleDTO</b-hint>
    </field>
</mapping>

Есть еще одна проблема. Когда Dozer пытается преобразовать роли из User в роли RoleDTO в UserDTO, я получаю исключение ленивой инициализации из Hibernate, потому что роли в User находятся в режиме EAGER.

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

person sylsau    schedule 14.05.2010
comment
Сделайте преобразование перед отсоединением сущностей, вот и все. - person Pascal Thivent; 14.05.2010
comment
Привет, Паскаль, что вы имеете в виду, говоря о преобразовании перед отсоединением сущностей? Вы имеете в виду отображение на уровне транзакций? - person javatar; 15.11.2011

Как говорит Паскаль, я не понимаю, как использовать PersistentBags напрямую. Если в вашем POJO @Entity вы объявляете свой список ролей как классический java.util.List, тогда Hibernate автоматически использует его внутреннее представление. Это также причина того, что вы не можете использовать свой POJO в процессе сериализации. Чтобы уточнить:

@Entity
public class User {
  private java.util.List<Role> roles = new java.util.Arraylist<Role>(); //Hibernate doesn't really care about this new Arraylist stuff, it will use its org.hibernate.collection.PersistentList
  ... //other fields and methods
}

Теперь, когда вы хотите преобразовать свой объект POJO Entity в объект DTO, вам просто нужно использовать сопоставление, предложенное Pascal, также известное как

<mapping>
  <class-a>com.myapp.domain.User</class-a>
  <class-b>com.myapp.dto.UserDTO</class-b>
  <field>
    <a>roles</a>
    <b>roles</b>
    <a-hint>com.myapp.domain.Role</a-hint>
    <b-hint>com.myapp.dto.RoleDTO</b-hint>
  </field>
</mapping>

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

@Transactional
public UserDTO getUser(...) {
  User user = dao.getUser(...);
  UserDTO dto = //Place your Dozer conversion here
  return dto;
}

Небольшое замечание, я использовал Dozer по той же причине, и это работало как шарм.

person ThanksForAllTheFish    schedule 03.01.2013
comment
Может ли это решение работать, если бы между UserDTO и User было несоответствие в количестве ролей? (например, роль была отозвана у пользователя через веб-интерфейс, поэтому объект UserDTO имеет на одну роль меньше, чем объект спящего режима пользователя) - person Taoufik Mohdit; 17.11.2014
comment
@TaoufikMohdit, не уверен, что полностью понимаю ваш вопрос, но да. По сути, когда вы конвертируете из UserDTO в User, вы не рассматриваете какую-либо базу данных. Затем (и это предположение), когда вы обновляете данные в базе данных, Hibernate позаботится об объединении вашей сущности с сохраненной и обновит базу данных: конечно, в вашем случае вы хотите сначала преобразовать из UserDTO в User а затем вы хотите выполнить вызовы db. В режиме гибернации: journaldev.com/3481/ - person ThanksForAllTheFish; 17.11.2014
comment
Спасибо за ваш ответ! Описанный вами подход будет работать с методом org.dozer.Mapper # map (источник объекта, класс ‹T› destinationClass), но при этом предполагается, что все атрибуты отображаются в DTO (в противном случае несопоставленные атрибуты будут аннулированы). Если некоторые атрибуты исключены из сопоставления, я думаю, что другой метод org.dozer.Mapper # map (источник объекта, назначение объекта) [где целевой объект - это объект, извлеченный из базы данных], будет работать. Однако, похоже, он не работает должным образом с атрибутами коллекции [атрибут ролей в приведенном выше примере] - person Taoufik Mohdit; 18.11.2014