Как работать с DTO в проектах Spring Data REST?
Spring Data REST автоматизирует отображение только объекта домена. Но чаще всего приходится иметь дело с объектами передачи данных. Итак,как это сделать в SDR?
1 ответов
подход, как работать с DTO на Весна данные проекты
рабочий пример здесь
объекты
субъекты должны реализовать идентифицируемых интерфейс. Например:
@Entity
public class Category implements Identifiable<Integer> {
@Id
@GeneratedValue
private final Integer id;
private final String name;
@OneToMany
private final Set<Product> products = new HashSet<>();
// skipped
}
@Entity
public class Product implements Identifiable<Integer> {
@Id
@GeneratedValue
private final Integer id;
private final String name;
// skipped
}
прогнозы
сделать проекция интерфейс, который методы запроса репозитория вернут:
public interface CategoryProjection {
Category getCategory();
Long getQuantity();
}
это будет подвал для DTO. В этом примере DTO будет представлять собой Category
и ряд Product
s принадлежат к нему.
методы репозитория
Create методы возвращают проекцию: один, список DTO и выгружаемый список DTO.
@RepositoryRestResource
public interface CategoryRepo extends JpaRepository<Category, Integer> {
@RestResource(exported = false)
@Query("select c as category, count(p) as quantity from Category c join c.products p where c.id = ?1 group by c")
CategoryProjection getDto(Integer categoryId);
@RestResource(exported = false)
@Query("select c as category, count(p) as quantity from Category c join c.products p group by c")
List<CategoryProjection> getDtos();
@RestResource(exported = false)
@Query("select c as category, count(p) as quantity from Category c join c.products p group by c")
Page<CategoryProjection> getDtos(Pageable pageable);
}
DTO
реализовать DTO из его интерфейс:
@Relation(value = "category", collectionRelation = "categories")
public class CategoryDto implements CategoryProjection {
private final Category category;
private final Long quantity;
// skipped
}
аннотации Relation
используется, когда Spring Data REST отрисовывает объект.
контроллер
добавить пользовательские методы RepositoryRestController
что будет обслуживать запросы DTO:
@RepositoryRestController
@RequestMapping("/categories")
public class CategoryController {
@Autowired private CategoryRepo repo;
@Autowired private RepositoryEntityLinks links;
@Autowired private PagedResourcesAssembler<CategoryProjection> assembler;
/**
* Single DTO
*/
@GetMapping("/{id}/dto")
public ResponseEntity<?> getDto(@PathVariable("id") Integer categoryId) {
CategoryProjection dto = repo.getDto(categoryId);
return ResponseEntity.ok(toResource(dto));
}
/**
* List of DTO
*/
@GetMapping("/dto")
public ResponseEntity<?> getDtos() {
List<CategoryProjection> dtos = repo.getDtos();
Link listSelfLink = links.linkFor(Category.class).slash("/dto").withSelfRel();
List<?> resources = dtos.stream().map(this::toResource).collect(toList());
return ResponseEntity.ok(new Resources<>(resources, listSelfLink));
}
/**
* Paged list of DTO
*/
@GetMapping("/dtoPaged")
public ResponseEntity<?> getDtosPaged(Pageable pageable) {
Page<CategoryProjection> dtos = repo.getDtos(pageable);
Link pageSelfLink = links.linkFor(Category.class).slash("/dtoPaged").withSelfRel();
PagedResources<?> resources = assembler.toResource(dtos, this::toResource, pageSelfLink);
return ResponseEntity.ok(resources);
}
private ResourceSupport toResource(CategoryProjection projection) {
CategoryDto dto = new CategoryDto(projection.getCategory(), projection.getQuantity());
Link categoryLink = links.linkForSingleResource(projection.getCategory()).withRel("category");
Link selfLink = links.linkForSingleResource(projection.getCategory()).slash("/dto").withSelfRel();
return new Resource<>(dto, categoryLink, selfLink);
}
}
когда проекции получены из репозитория, мы должны сделать окончательное преобразование из проекции в DTO и "оберните" его в ResourceSupport