Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/cepr0/spring-data-rest-dto
An approach of how to work with DTO in Spring Data REST projects
https://github.com/cepr0/spring-data-rest-dto
dto projection rest spring-boot spring-data-jpa spring-data-rest
Last synced: 18 days ago
JSON representation
An approach of how to work with DTO in Spring Data REST projects
- Host: GitHub
- URL: https://github.com/cepr0/spring-data-rest-dto
- Owner: Cepr0
- Created: 2017-07-20T21:14:28.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2017-07-30T19:53:32.000Z (over 7 years ago)
- Last Synced: 2024-10-13T02:43:18.133Z (about 1 month ago)
- Topics: dto, projection, rest, spring-boot, spring-data-jpa, spring-data-rest
- Language: Java
- Homepage:
- Size: 47.9 KB
- Stars: 45
- Watchers: 6
- Forks: 11
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# Spring Data REST and DTO
_An approach of how to work with [DTO][1] in [Spring Data REST][2] projects_
## Entities
Entities must implement the [Identifiable][3] interface. For example:
```java
@Entity
public class Category implements Identifiable {
@Id
@GeneratedValue
private final Integer id;
private final String name;
@OneToMany
private final Set products = new HashSet<>();
// skipped
}@Entity
public class Product implements Identifiable {@Id
@GeneratedValue
private final Integer id;
private final String name;
// skipped
}
```## Projections
Make a [projection][4] interface that repository query methods will return:
```java
public interface CategoryProjection {Category getCategory();
Long getQuantity();
}
```It will be a basement for DTO. DTO will represent a `Category` and the number of `Product`s are belong to it.
## Repository methods
Create methods return the projection: a single one, a list of DTO and a paged list of DTO.
```java
@RepositoryRestResource
public interface CategoryRepo extends JpaRepository {
@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 getDtos();
@RestResource(exported = false)
@Query("select c as category, count(p) as quantity from Category c join c.products p group by c")
Page getDtos(Pageable pageable);
}
```## DTO
Implement DTO from its interface:
```java
@Relation(value = "category", collectionRelation = "categories")
public class CategoryDto implements CategoryProjection {private final Category category;
private final Long quantity;
// skipped
}
```Annotation `Relation` is used when Spring Data REST is rendering the object.
## Controller
Add custom methods to `RepositoryRestController` that will serve requests of DTO:
```java
@RepositoryRestController
@RequestMapping("/categories")
public class CategoryController {@Autowired private CategoryRepo repo;
@Autowired private RepositoryEntityLinks links;
@Autowired private PagedResourcesAssembler 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 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 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);
}
}
```When Projections are received from repository we must make the final transformation from a Projection to DTO
and 'wrap' it to [ResourceSupport][5] object before sending to the client.
To do this we use helper method `toResource`: we create a new DTO, create necessary links for this object,
and then create a new `Resource` with the object and its links.## Result
_See the API docs on the [Postman site](https://documenter.getpostman.com/view/788154/spring-data-rest-dto/6mz3FWE)_
### Singe DTO
GET http://localhost:8080/api/categories/6/dto
```json
{
"category": {
"name": "category1"
},
"quantity": 3,
"_links": {
"category": {
"href": "http://localhost:8080/api/categories/6"
},
"self": {
"href": "http://localhost:8080/api/categories/6/dto"
}
}
}
```### List of DTO
GET http://localhost:8080/api/categories/dto
```json
{
"_embedded": {
"categories": [
{
"category": {
"name": "category1"
},
"quantity": 3,
"_links": {
"category": {
"href": "http://localhost:8080/api/categories/6"
},
"self": {
"href": "http://localhost:8080/api/categories/6/dto"
}
}
},
{
"category": {
"name": "category2"
},
"quantity": 2,
"_links": {
"category": {
"href": "http://localhost:8080/api/categories/7"
},
"self": {
"href": "http://localhost:8080/api/categories/7/dto"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/categories/dto"
}
}
}
```### Paged list of DTO
GET http://localhost:8080/api/categories/dtoPaged
```json
{
"_embedded": {
"categories": [
{
"category": {
"name": "category1"
},
"quantity": 3,
"_links": {
"category": {
"href": "http://localhost:8080/api/categories/6"
},
"self": {
"href": "http://localhost:8080/api/categories/6/dto"
}
}
},
{
"category": {
"name": "category2"
},
"quantity": 2,
"_links": {
"category": {
"href": "http://localhost:8080/api/categories/7"
},
"self": {
"href": "http://localhost:8080/api/categories/7/dto"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/api/categories/dtoPaged"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
```[1]: https://en.wikipedia.org/wiki/Data_transfer_object
[2]: https://projects.spring.io/spring-data-rest/
[3]: http://docs.spring.io/spring-hateoas/docs/current-SNAPSHOT/api/org/springframework/hateoas/Identifiable.html
[4]: https://spring.io/blog/2016/05/03/what-s-new-in-spring-data-hopper#projections-on-repository-query-methods
[5]: http://docs.spring.io/spring-hateoas/docs/current-SNAPSHOT/api/org/springframework/hateoas/ResourceSupport.html