mapstruct通过将子dto与现有子实体合并来更新映射一对多关系

时间:2018-04-27 21:02:45

标签: java mapping mapstruct

我有两个具有一对多关系的实体,如下所示:

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 :(得分:1)

我的porpouse是通过合并现有的子实体来执行一对多实体关系的更新,假设我们在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

}

和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;
    }
}

出现在我脑海中的第一个解决方案是通过合并子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的孩子关系?

答案 1 :(得分:0)

目前无法做到这一点。对此已经存在一个悬而未决的问题。看看487

执行2个列表的合并并非易事。它需要MapStruct知道如何生成该find方法。