Обновление mapstruct отображает отношение один-ко-многим путем слияния дочернего dto с существующими дочерними объектами - PullRequest
0 голосов
/ 28 апреля 2018

У меня есть две сущности с отношением один-ко-многим, как указано ниже:

public class ParentEntity {

   private Long id;
   private String name;
   private List<ChildEntity> children;

   //getters/setters

}

public class ChildEntity {

    private Long id;
    private String name;
    private ParentEntity myParent;

    private String notMappedField1;
    private Long notMappedField2;

    //getters/setters

}

И DTO

public class ParentDTO {

       private Long id;
       private String name;
       private List<ChildDto> children;

       //getters/setters

    }

    public class ChildDto {

        private Long id;
        private String name;
        private ParentDto myParent;

        //getters/setters

    }

С помощью аннотаций Mapstruct я создал два картографа, по одному для каждой сущности:

   @Mapper(componentModel = "cdi", uses = {ChildMapper.class})
   public interface ParentMapper {      
       ParentDto toDto(ParentEntity entity);     
       ParentEntity toEntity(ParentDto s);      
       ParentEntity toEntity(ParentDto s, @MappingTarget ParentEntity entity);           
   }

   @Mapper(componentModel = "cdi")
   public interface ChildMapper {

       ChildDto toDto(ChildEntity entity);

       @Mapping(target = "myParent", ignore = true)
       ChildEntity toEntity(ChildDto s);

       @InheritConfiguration
       ChildEntity toEntity(ChildDto s, @MappingTarget ChildEntity entity);

   }

Генерируемый родительский маппер выглядит следующим образом:

@ApplicationScoped
public class ParentMapperImpl implements ParentMapper {

    @Inject
    private ChildMapper childMapper;

    @Override
    public ParentDto toDto(ParentEntity entity) {
        if ( entity == null ) {
            return null;
        }

        ParentDto parentDto = new ParentDto();

        parentDto.setId( entity.getId() );
        parentDto.setName( entity.getName() );
        parentDto.setChildren( childEntityListToChildDtoList( entity.getChildren() ) );

        return parentDto;
    }

    @Override
    public ParentEntity toEntity(ParentDto s) {
        if ( s == null ) {
            return null;
        }

        ParentEntity parentEntity = new ParentEntity();

        parentEntity.setId( s.getId() );
        parentEntity.setName( s.getName() );
        parentEntity.setChildren( childDtoListToChildEntityList( s.getChildren() ) );

        return parentEntity;
    }

    @Override
    public ParentEntity toEntity(ParentDto s, ParentEntity entity) {
        if ( s == null ) {
            return null;
        }

        entity.setId( s.getId() );
        entity.setName( s.getName() );
        if ( entity.getChildren() != null ) {
            List<ChildEntity> list = childDtoListToChildEntityList( s.getChildren() );
            if ( list != null ) {
                entity.getChildren().clear();
                entity.getChildren().addAll( list );
            }
            else {
                entity.setChildren( null );
            }
        }
        else {
            List<ChildEntity> list = childDtoListToChildEntityList( s.getChildren() );
            if ( list != null ) {
                entity.setChildren( list );
            }
        }

        return entity;
    }

    protected List<ChildDto> childEntityListToChildDtoList(List<ChildEntity> list) {
        if ( list == null ) {
            return null;
        }

        List<ChildDto> list1 = new ArrayList<ChildDto>( list.size() );
        for ( ChildEntity childEntity : list ) {
            list1.add( childMapper.toDto( childEntity ) );
        }

        return list1;
    }

    protected List<ChildEntity> childDtoListToChildEntityList(List<ChildDto> list) {
        if ( list == null ) {
            return null;
        }

        List<ChildEntity> list1 = new ArrayList<ChildEntity>( list.size() );
        for ( ChildDto childDto : list ) {
            list1.add( childMapper.toEntity( childDto ) );
        }

        return list1;
    }
}

Но когда ParentMapper.toEntity(ParentDto s, ParentEntity entity) называется, на ребенке маппер называется ChildMapper.toEntity(ChildDto s).

Вместо этого я бы хотел, чтобы вызывался следующий дочерний метод отображения ChildMapper.toEntity(ChildDto s, ChildEntity entity), объединить существующие дочерние объекты (скажем, у нас есть другие поля на дочерние объекты, которых нет в Dto, которые мы не хотим терять).
Желаемый результат следующий или эквивалентный:

@ApplicationScoped
public class ParentMapperImpl implements ParentMapper {

      ...

      @Override
      public ParentEntity toEntity(ParentDto s, ParentEntity entity) {
          if (s == null) {
              return null;
          }

          entity.setId(s.getId());
          entity.setName(s.getName());
          if (entity.getChildren() != null) {
              List<ChildEntity> list = childDtoListToChildEntityList(s.getChildren(), entity.getChildren());
              if (list != null) {
                  entity.getChildren().clear();
                  entity.getChildren().addAll(list);
              } else {
                  entity.setChildren(null);
              }
          } else {
              List<ChildEntity> list = childDtoListToChildEntityList(s.getChildren());
              if (list != null) {
                  entity.setChildren(list);
              }
          }

          return entity;
      }

      ...

      protected List<ChildEntity> childDtoListToChildEntityList(List<ChildDto> list, List<ChildEntity> entities) {
          if (list == null) {
              return null;
          }


          final List<ChildEntity> list1 = new ArrayList<ChildEntity>(list.size());
          for (ChildEntity entity : entities) {
              Optional<ChildEntity> optional = findEntity(entity.getId(), list);
              if (optional.isPresent()) {
                  list1.add(childMapper.toEntity(optional.get(), entity));
              }
          }

          list1.addAll(
                  list.stream()
                          .filter(dto -> dto.getId() == null)
                          .map(dto -> childMapper.toEntity(dto))
                          .collect(Collectors.toList())
          );


          return list1;
      }

      private Optional<ChildEntity> findEntity(Long id, List<ChildDto> list) {
          return list.stream().filter(dto -> dto.getId().equals(id)).findAny();
      }

}          

Ответы [ 2 ]

0 голосов
/ 28 апреля 2018

Моя задача состоит в обновлении отношения сущностей «один ко многим» путем объединения существующих дочерних сущностей. Допустим, у нас есть другие поля в дочерних сущностях, которых нет в Dto, которые мы не хотим потерять ,

У меня есть две сущности с отношением один-ко-многим, как указано ниже:

public class ParentEntity {

   private Long id;
   private String name;
   private List<ChildEntity> children;

   //getters/setters

}

public class ChildEntity {

    private Long id;
    private String name;
    private ParentEntity myParent;

    private String notMappedField1;
    private Long notMappedField2;

    //getters/setters

}

И DTOs

public class ParentDTO {

       private Long id;
       private String name;
       private List<ChildDto> children;

       //getters/setters

    }

    public class ChildDto {

        private Long id;
        private String name;
        private ParentDto myParent;

        //getters/setters

    }

С помощью аннотаций Mapstruct я создал два картографа, по одному для каждой сущности:

   @Mapper(componentModel = "cdi", uses = {ChildMapper.class})
   public interface ParentMapper {      
       ParentDto toDto(ParentEntity entity);     
       ParentEntity toEntity(ParentDto s);      
       ParentEntity toEntity(ParentDto s, @MappingTarget ParentEntity entity);           
   }

   @Mapper(componentModel = "cdi")
   public interface ChildMapper {

       ChildDto toDto(ChildEntity entity);

       @Mapping(target = "myParent", ignore = true)
       ChildEntity toEntity(ChildDto s);

       @InheritConfiguration
       ChildEntity toEntity(ChildDto s, @MappingTarget ChildEntity entity);

   }

Сгенерированный родительский маппер выглядит следующим образом:

@ApplicationScoped
public class ParentMapperImpl implements ParentMapper {

    @Inject
    private ChildMapper childMapper;

    @Override
    public ParentDto toDto(ParentEntity entity) {
        if ( entity == null ) {
            return null;
        }

        ParentDto parentDto = new ParentDto();

        parentDto.setId( entity.getId() );
        parentDto.setName( entity.getName() );
        parentDto.setChildren( childEntityListToChildDtoList( entity.getChildren() ) );

        return parentDto;
    }

    @Override
    public ParentEntity toEntity(ParentDto s) {
        if ( s == null ) {
            return null;
        }

        ParentEntity parentEntity = new ParentEntity();

        parentEntity.setId( s.getId() );
        parentEntity.setName( s.getName() );
        parentEntity.setChildren( childDtoListToChildEntityList( s.getChildren() ) );

        return parentEntity;
    }

    @Override
    public ParentEntity toEntity(ParentDto s, ParentEntity entity) {
        if ( s == null ) {
            return null;
        }

        entity.setId( s.getId() );
        entity.setName( s.getName() );
        if ( entity.getChildren() != null ) {
            List<ChildEntity> list = childDtoListToChildEntityList( s.getChildren() );
            if ( list != null ) {
                entity.getChildren().clear();
                entity.getChildren().addAll( list );
            }
            else {
                entity.setChildren( null );
            }
        }
        else {
            List<ChildEntity> list = childDtoListToChildEntityList( s.getChildren() );
            if ( list != null ) {
                entity.setChildren( list );
            }
        }

        return entity;
    }

    protected List<ChildDto> childEntityListToChildDtoList(List<ChildEntity> list) {
        if ( list == null ) {
            return null;
        }

        List<ChildDto> list1 = new ArrayList<ChildDto>( list.size() );
        for ( ChildEntity childEntity : list ) {
            list1.add( childMapper.toDto( childEntity ) );
        }

        return list1;
    }

    protected List<ChildEntity> childDtoListToChildEntityList(List<ChildDto> list) {
        if ( list == null ) {
            return null;
        }

        List<ChildEntity> list1 = new ArrayList<ChildEntity>( list.size() );
        for ( ChildDto childDto : list ) {
            list1.add( childMapper.toEntity( childDto ) );
        }

        return list1;
    }
}

Первое решение, которое мне пришло в голову, - это обновить список дочерних сущностей, объединив дочерний список dto и список дочерних сущностей, поэтому я подумал, что когда вызывается ParentMapper.toEntity(ParentDto s, ParentEntity entity), метод дочернего сопоставления ChildMapper.toEntity(ChildDto s, ChildEntity entity) должен быть вызван, чтобы объединить существующие дочерние сущности, а не эту ChildMapper.toEntity(ChildDto s).
Таким образом, результатом может быть следующее или что-то эквивалентное:

@ApplicationScoped
public class ParentMapperImpl implements ParentMapper {

      ...

      @Override
      public ParentEntity toEntity(ParentDto s, ParentEntity entity) {
          if (s == null) {
              return null;
          }

          entity.setId(s.getId());
          entity.setName(s.getName());
          if (entity.getChildren() != null) {
              List<ChildEntity> list = childDtoListToChildEntityList(s.getChildren(), entity.getChildren());
              if (list != null) {
                  entity.getChildren().clear();
                  entity.getChildren().addAll(list);
              } else {
                  entity.setChildren(null);
              }
          } else {
              List<ChildEntity> list = childDtoListToChildEntityList(s.getChildren());
              if (list != null) {
                  entity.setChildren(list);
              }
          }

          return entity;
      }

      ...

      protected List<ChildEntity> childDtoListToChildEntityList(List<ChildDto> list, List<ChildEntity> entities) {
          if (list == null) {
              return null;
          }


          final List<ChildEntity> list1 = new ArrayList<ChildEntity>(list.size());
          for (ChildEntity entity : entities) {
              Optional<ChildEntity> optional = findEntity(entity.getId(), list);
              if (optional.isPresent()) {
                  list1.add(childMapper.toEntity(optional.get(), entity));
              }
          }

          list1.addAll(
                  list.stream()
                          .filter(dto -> dto.getId() == null)
                          .map(dto -> childMapper.toEntity(dto))
                          .collect(Collectors.toList())
          );


          return list1;
      }

      private Optional<ChildEntity> findEntity(Long id, List<ChildDto> list) {
          return list.stream().filter(dto -> dto.getId().equals(id)).findAny();
      }

}          

Возможно ли это с Mapstruct? Есть ли другой способ обновить отношения детей с Mapstruct?

0 голосов
/ 28 апреля 2018

В настоящее время это невозможно. Для этого уже есть открытый вопрос. Посмотрите на 487 .

Совмещение двух списков не так уж и просто. MapStruct должен знать, как генерировать этот метод поиска.

...