简体   繁体   中英

How to map Page<ObjectEntity> to Page<ObjectDTO> in spring-data-rest

When I hit the database with PagingAndSortingRepository.findAll(Pageable) I get Page<ObjectEntity> . However, I want to expose DTO's to the client and not entities. I can create DTO just by injecting entity into it's constructor, but how do I map the entities in Page object to DTO's? According to spring documentation, Page provides read-only operations.

Also, Page.map is not possibility, as we don't have support for java 8. How to create the new Page with mapped objects manually?

You can still use the Page.map without lambda expressions:

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

In Spring Data 2, the Page map method takes a Function instead of a Converter, but it still works basically the same as @Ali Dehghani described.

Using Function:

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

And in java8:

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

Where fromEntity is a static method on ObjectDto that contains the conversion logic.

You can use Page.map by simply doing this:

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

Here is my solution, thanks to @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);
            }

        });
    }

I am created and using solution with model mapper, generics and lambdas for common usage.

/**
 * 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));
} 

This is exactly the case which you need (and I think common case for wide range of other cases).

You already have the data obtained from repository (same is with service) on this way:

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

Everything what you need for conversion is to call this method on this way:

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

@Tip: You can use this method from util class, and it can be reused for all entity/dto in Page conversions on services and controllers according to your architecture.

Example:

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

This works correctly in 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);
            }

        });
    }

The converter is no longer supported in Page type for Spring 2.0. Also, the Function should be used from java.util.function.Function.

使用 lambda 表达式更方便

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

Page<OrderDTO> orderPage = persistedOrderPage.map(persistedOrder -> {
    OrderDTO order = mapper.toOrderDTO(persistedOrder);
    // do another action
    return order;
});

At the end, you will not return the Page to the users, but a list of ObjectDTO, with the Page details at the header, so this would be my solution.

ObjectService

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

ObjectResource / rest (the exposed endpoint)

@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);
}

The reason for using this is so that you don't need to duplicate the page details for ObjectEntity and DTO. It is key to note that a page contains the following:

  • page number
  • pageSize
  • numberOfElements
  • content

The content is the list of objects returned, and is the only thing that needs to be mapped to DTO.

Using Java 8 Lambda ,It worked for me. The answer is already given above,I am just simplifying.

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


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

Here employeeEntityToDto() is a method to convert Entities to Dtos

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

It's a too late answer, but this solution worked for me

Service

public Page<EntityDto> getAllEntities(Pageable pageable) {
    return entityRepository.findAll(pageable).map(entityMapper::toEntityDto);
}

Mapper

import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface EntityMapper {
    EntityDto toEntityDto(Entity entity);
}

Controller

@GetMapping(path = "/entities")
    public List<EntityDto> getAllEntities(Pageable pageable) {
        Page<EntityDto> page = entityService.getAllEntities(pageable);
        return page.getContent();
    }

this will return a list of entities with size = 10 from the first page

http://localhost:port/api/entities?page=0&size=10

Thanks to JHipster!

I've used Lambda with ModelMapper

    Page<ObjectEntity> pageEntity = objectRepository.findAll(pageable);
    Page<ObjectDto> pageDto = pageEntity.map(objectEntity -> modelMapper.map(objectEntity, ObjectDto.class));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM