1. Обзор
В этом руководстве мы рассмотрим преобразования, которые должны произойти между внутренними сущностями приложения Spring и внешними DTO (объектами передачи данных), которые публикуются обратно клиенту.
Дальнейшее чтение:
Аннотации Spring’s RequestBody и ResponseBody
Краткое руководство по MapStruct
2. Картограф моделей
Давайте начнем с представления основной библиотеки, которую мы собираемся использовать для выполнения этого преобразования entity-DTO, ModelMapper .
Нам понадобится эта зависимость в pom.xml :
org.modelmapper modelmapper 2.3.5
Чтобы проверить, есть ли какая-либо более новая версия этой библиотеки, перейдите сюда .
Затем мы определим ModelMapper bean в нашей весенней конфигурации:
@Bean public ModelMapper modelMapper() { return new ModelMapper(); }
3. DTO
Далее давайте представим ВЕРХНЮЮ сторону этой двусторонней проблемы, Post TO:
public class PostDto { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); private Long id; private String title; private String url; private String date; private UserDto user; public Date getSubmissionDateConverted(String timezone) throws ParseException { dateFormat.setTimeZone(TimeZone.getTimeZone(timezone)); return dateFormat.parse(this.date); } public void setSubmissionDate(Date date, String timezone) { dateFormat.setTimeZone(TimeZone.getTimeZone(timezone)); this.date = dateFormat.format(date); } // standard getters and setters }
Обратите внимание, что два пользовательских метода, связанных с датой, обрабатывают преобразование данных между клиентом и сервером:
- get Submission Date Converted() метод преобразует дату Строку в Дату в часовом поясе сервера, чтобы использовать ее в сохраняющейся Post сущности
- установить дату отправки() метод состоит в том, чтобы установить данные DTO в Post ‘s Date в текущем часовом поясе пользователя
4. Уровень Обслуживания
Теперь давайте рассмотрим операцию уровня обслуживания, которая, очевидно, будет работать с сущностью (а не с):
public ListgetPostsList( int page, int size, String sortDir, String sort) { PageRequest pageReq = PageRequest.of(page, size, Sort.Direction.fromString(sortDir), sort); Page posts = postRepository .findByUser(userService.getCurrentUser(), pageReq); return posts.getContent(); }
Далее мы рассмотрим слой над сервисом, уровень контроллера. Именно здесь на самом деле произойдет преобразование.
5. Уровень Контроллера
Далее давайте рассмотрим стандартную реализацию контроллера, раскрывающую простой REST API для ресурса Post .
Мы покажем здесь несколько простых операций CRUD: создание, обновление, получение одного и получение всех. Учитывая, что операции довольно просты, нас особенно интересуют аспекты преобразования сущности в DTO :
@Controller class PostRestController { @Autowired private IPostService postService; @Autowired private IUserService userService; @Autowired private ModelMapper modelMapper; @GetMapping @ResponseBody public ListgetPosts(...) { //... List posts = postService.getPostsList(page, size, sortDir, sort); return posts.stream() .map(this::convertToDto) .collect(Collectors.toList()); } @PostMapping @ResponseStatus(HttpStatus.CREATED) @ResponseBody public PostDto createPost(@RequestBody PostDto postDto) { Post post = convertToEntity(postDto); Post postCreated = postService.createPost(post)); return convertToDto(postCreated); } @GetMapping(value = "/{id}") @ResponseBody public PostDto getPost(@PathVariable("id") Long id) { return convertToDto(postService.getPostById(id)); } @PutMapping(value = "/{id}") @ResponseStatus(HttpStatus.OK) public void updatePost(@RequestBody PostDto postDto) { Post post = convertToEntity(postDto); postService.updatePost(post); } }
Вот наше преобразование из Post entity в PostDto :
private PostDto convertToDto(Post post) { PostDto postDto = modelMapper.map(post, PostDto.class); postDto.setSubmissionDate(post.getSubmissionDate(), userService.getCurrentUser().getPreference().getTimezone()); return postDto; }
Вот преобразование из DTO в сущность :
private Post convertToEntity(PostDto postDto) throws ParseException { Post post = modelMapper.map(postDto, Post.class); post.setSubmissionDate(postDto.getSubmissionDateConverted( userService.getCurrentUser().getPreference().getTimezone())); if (postDto.getId() != null) { Post oldPost = postService.getPostById(postDto.getId()); post.setRedditID(oldPost.getRedditID()); post.setSent(oldPost.isSent()); } return post; }
Таким образом, как мы видим, с помощью картографа моделей логика преобразования быстра и проста. Мы используем map API картографа и получаем данные, преобразованные без написания одной строки логики преобразования.
6. Модульное тестирование
Наконец, давайте проведем очень простой тест, чтобы убедиться, что преобразования между сущностью и объектом работают хорошо:
public class PostDtoUnitTest { private ModelMapper modelMapper = new ModelMapper(); @Test public void whenConvertPostEntityToPostDto_thenCorrect() { Post post = new Post(); post.setId(1L); post.setTitle(randomAlphabetic(6)); post.setUrl("www.test.com"); PostDto postDto = modelMapper.map(post, PostDto.class); assertEquals(post.getId(), postDto.getId()); assertEquals(post.getTitle(), postDto.getTitle()); assertEquals(post.getUrl(), postDto.getUrl()); } @Test public void whenConvertPostDtoToPostEntity_thenCorrect() { PostDto postDto = new PostDto(); postDto.setId(1L); postDto.setTitle(randomAlphabetic(6)); postDto.setUrl("www.test.com"); Post post = modelMapper.map(postDto, Post.class); assertEquals(postDto.getId(), post.getId()); assertEquals(postDto.getTitle(), post.getTitle()); assertEquals(postDto.getUrl(), post.getUrl()); } }
7. Заключение
В этой статье мы подробно описали упрощение преобразования из Entity в DTO и из DTO в Entity в API Spring REST , используя библиотеку model mapper вместо написания этих преобразований вручную.
Полный исходный код примеров доступен в проекте GitHub .