如何在 spring-data-rest 中将 Page<ObjectEntity> 映射到 Page<ObjectDTO>

Posted

技术标签:

【中文标题】如何在 spring-data-rest 中将 Page<ObjectEntity> 映射到 Page<ObjectDTO>【英文标题】:How to map Page<ObjectEntity> to Page<ObjectDTO> in spring-data-rest 【发布时间】:2016-12-26 11:32:20 【问题描述】:

当我用PagingAndSortingRepository.findAll(Pageable) 访问数据库时,我得到Page&lt;ObjectEntity&gt;。但是,我想向客户端而不是实体公开 DTO。我可以通过将实体注入它的构造函数来创建 DTO,但是如何将 Page 对象中的实体映射到 DTO?根据spring文档,Page提供了只读操作。

另外,Page.map 是不可能的,因为我们不支持 java 8。如何手动创建带有映射对象的新页面?

【问题讨论】:

我不确定,但我猜你可以在没有 lambda 表达式的情况下使用 Page.map。只需传递Converter&lt;? super T, ? extends S&gt; 的实例 【参考方案1】:

您仍然可以在没有 lambda 表达式的情况下使用 Page.map

Page<ObjectEntity> entities = objectEntityRepository.findAll(pageable);
Page<ObjectDto> dtoPage = entities.map(new Converter<ObjectEntity, ObjectDto>() 
    @Override
    public ObjectDto convert(ObjectEntity entity) 
        ObjectDto dto = new ObjectDto();
        // Conversion logic

        return dto;
    
);

【讨论】:

嗨@Ali我不确定我应该导入哪个Covnerter类。请问您能解开这个疑问吗? @MartinLarizzate 显然新的 Spring Data 版本正在使用简单的 Java 8 Functions。所以看看这里github.com/spring-projects/spring-data-commons/blob/… 感谢您帮助 Ali Dehgani 什么是转换器包? @Dhanushkasasanka 可能您正在使用 Spring Data 2,请参阅 ***.com/a/48831688/1393484【参考方案2】:

感谢@Ali Dehghani,这是我的解决方案

private Page<ObjectDTO> mapEntityPageIntoDTOPage(Page<ObjectEntity> objectEntityPage) 
        return objectEntityPage.map(new Converter<ObjectEntity, ObjectDTO>() 
            public ObjectDTO convert(ObjectEntity objectEntity) 
                return new ObjectDTO(objectEntity, httpSession);
            

        );
    

【讨论】:

【参考方案3】:

在 java8 中:

Page<ObjectDto> entities = 
 objectEntityRepository.findAll(pageable)
 .map(ObjectDto::fromEntity);

其中 fromEntity 是 ObjectDto 上包含转换逻辑的静态方法。

【讨论】:

要使用 OP 请求的 DTO 构造函数,您可以使用 .map(ObjectDto::new);而不是 .map(ObjectDto::fromEntity); (这假设您在 ObjectDto 中定义了正确的构造函数)【参考方案4】:

在 Spring Data 2 中,Page map 方法采用 Function 而不是 Converter,但它仍然与 @Ali Dehghani 描述的基本相同。

使用功能:

Page<ObjectEntity> entities = objectEntityRepository.findAll(pageable);
Page<ObjectDto> dtoPage = entities.map(new Function<ObjectEntity, ObjectDto>() 
    @Override
    public ObjectDto apply(ObjectEntity entity) 
        ObjectDto dto = new ObjectDto();
        // Conversion logic

        return dto;
    
);

【讨论】:

这里的函数包是什么?【参考方案5】:

最后,您不会将页面返回给用户,而是返回一个 ObjectDTO 列表,标题中包含页面详细信息,所以这将是我的解决方案。

对象服务

public Page<ObjectEntity> findAll (Pageable pageable)
  //logic goes here.
  Page<ObjectEntity> page = objectRepository.findAll(pageable);
  return page;
 

ObjectResource / rest(暴露的端点)

@GetMapping
public ResponseEntity<List<ObjectDTO>> findAll (Pageable pageable)
  Page<ObjectEntity> page = objectServiceService.findAll(pageable);

  HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "your-endpoint-here");

  return new ResponseEntity<>(objectMapper.toDto(page.getContent()), headers, HttpStatus.OK);

使用它的原因是您不需要复制 ObjectEntity 和 DTO 的页面详细信息。需要注意的是,一个页面包含以下内容:

页码 页面大小 元素数量 内容

content是返回的对象列表,是唯一需要映射到DTO的东西。

【讨论】:

【参考方案6】:

您只需执行以下操作即可使用 Page.map:

public Page<ObjectDto> toPageObjectDto(Page<Object> objects) 
    Page<ObjectDto> dtos  = objects.map(this::convertToObjectDto);
    return dtos;


private ObjectDto convertToObjectDto(Object o) 
    ObjectDto dto = new ObjectDto();
    //conversion here
    return dto;

【讨论】:

【参考方案7】:

我创建并使用具有模型映射器、泛型和 lambdas 的解决方案以供常用。

/**
 * Maps the Page @code entities of <code>T</code> type which have to be mapped as input to @code dtoClass Page
 * of mapped object with <code>D</code> type.
 *
 * @param <D> - type of objects in result page
 * @param <T> - type of entity in <code>entityPage</code>
 * @param entities - page of entities that needs to be mapped
 * @param dtoClass - class of result page element
 * @return page - mapped page with objects of type <code>D</code>.
 * @NB <code>dtoClass</code> must has NoArgsConstructor!
 */
public <D, T> Page<D> mapEntityPageIntoDtoPage(Page<T> entities, Class<D> dtoClass) 
    return entities.map(objectEntity -> modelMapper.map(objectEntity, dtoClass));
 

这正是您需要的情况(我认为其他情况的常见情况)。

您已经通过这种方式从存储库中获得了数据(服务也是如此):

Page<ObjectEntity> entities = objectEntityRepository.findAll(pageable);

转换所需的一切就是以这种方式调用此方法:

Page<ObjectDto> dtoPage = mapEntityPageIntoDtoPage(entities, ObjectDto.class);

@Tip:您可以从 util 类中使用此方法,并且可以根据您的架构在服务和控制器上的页面转换中将其重用于所有实体/dto。

例子:

Page<ObjectDto> dtoPage = mapperUtil.mapEntityPageIntoDtoPage(entities, ObjectDto.class);

【讨论】:

【参考方案8】:

使用lambda表达式更方便

Page<ObjectDto> dto=objectRepository.findAll(pageable).map((object -> DozerBeanMapperBuilder.buildDefault().map(object, ObjectDto.class)));

【讨论】:

【参考方案9】:

这在 Spring 2.0 中可以正常工作 -

@Override
public Page<BookDto> getBooksByAuthor(String authorId, Pageable pageable) 
        Page<BookEntity> bookEntity = iBookRepository.findByAuthorId(authorId, pageable);
        return bookEntity.map(new Function<BookEntity, BookDto>() 

            @Override
            public BookDto apply(BookEntity t) 
                return new ModelMapper().map(t, BookDto.class);
            

        );
    

Spring 2.0 的页面类型不再支持转换器。另外,函数应该从 java.util.function.Function 中使用。

【讨论】:

【参考方案10】:

使用 Java 8 Lambda,它对我有用。上面已经给出了答案,我只是在简化。

Page<EmployeeEntity> employeeEntityPage = employeeService.findEmployeeEntities();


Page<EmployeeDto> employeeDtoPage = employeeEntityPage.map(entity -> 
        EmployeeDto dto = employeeService.employeEntityToDto(entity);
        return dto;
    );

这里的employeeEntityToDto()是Entities转Dtos的方法

public EmployeeDto employeeEntityToDto(EmployeeEntity entity)
    EmployeeDto employeeDto =  new EmployeeDto();
    employeeDto.setId(entity.getId());
    employeeDto.setName(entity.getName());
    return employeeDto;

【讨论】:

以上是关于如何在 spring-data-rest 中将 Page<ObjectEntity> 映射到 Page<ObjectDTO>的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring-Data-Rest 中实现细粒度的访问控制?

如何配置 Spring-Data-Rest 以仅返回登录用户拥有的数据

如何将 spring-data-rest 与 spring websocket 混合到一个实现中

Spring-Data-Rest 验证器

使用 spring-data-rest 将资源添加到集合

处理事务中的 spring-data-rest 应用程序事件