Entity To DTO Conversion for a Spring REST API
2. Model Mapper
Let"s start by introducing the main library that we"re going to use to perform this entity-DTO conversion – ModelMapper.
We will need this dependency in the pom.xml:
1
2
3
4
5
|
<
dependency
>
<
groupId
>org.modelmapper</
groupId
>
<
artifactId
>modelmapper</
artifactId
>
<
version
>2.3.5</
version
>
</
dependency
>
|
To check if there"s any newer version of this library, go here.
We"ll then define the ModelMapper bean in our Spring configuration:
1
2
3
4
|
3. The DTO
Next, let"s introduce the DTO side of this two-sided problem – Post DTO:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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
}
|
Note that the two custom date related methods handle the date conversion back and forth between the client and the server:
- getSubmissionDateConverted() method converts date String into a Date in server"s timezone to use it in the persisting Post entity
- setSubmissionDate() method is to set DTO"s date to Post‘s Date in current user timezone.
4. The Service Layer
Let"s now look at a service level operation – which will obviously work with the Entity (not the DTO):
1
2
3
4
5
6
7
8
9
10
|
public
List<Post> getPostsList(
int
page,
int
size, String sortDir, String sort) {
PageRequest pageReq
= PageRequest.of(page, size, Sort.Direction.fromString(sortDir), sort);
Page<Post> posts = postRepository
.findByUser(userService.getCurrentUser(), pageReq);
return
posts.getContent();
}
|
We"re going to have a look at the layer above service next – the controller layer. This is where the conversion will actually happen as well.
5. The Controller Layer
Let"s now have a look at a standard controller implementation, exposing the simple REST API for the Post resource.
We"re going to show here a few simple CRUD operations: create, update, get one and get all. And given the operations are pretty straightforward, we are especially interested in the Entity-DTO conversion aspects:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
class
PostRestController {
@Autowired
private
IPostService postService;
@Autowired
private
IUserService userService;
@Autowired
private
ModelMapper modelMapper;
@GetMapping
@ResponseBody
public
List<PostDto> getPosts(...) {
//...
List<Post> 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);
}
}
|
And here is our conversion from Post entity to PostDto:
1
2
3
4
5
6
|
private
PostDto convertToDto(Post post) {
PostDto postDto = modelMapper.map(post, PostDto.
class
);
postDto.setSubmissionDate(post.getSubmissionDate(),
userService.getCurrentUser().getPreference().getTimezone());
return
postDto;
}
|
And here is the conversion from DTO to an entity:
1
2
3
4
5
6
7
8
9
10
11
12
|
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;
}
|
So, as you can see, with the help of the model mapper, the conversion logic is quick and simple – we"re using the map API of the mapper and getting the data converted without writing a single line of conversion logic.
6. Unit Testing
Finally, let"s do a very simple test to make sure the conversions between the entity and the DTO work well:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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. Conclusion
This was an article on simplifying the conversion from Entity to DTO and from DTO to Entity in a Spring REST API, by using the model mapper library instead of writing these conversions by hand.
The full source code for the examples is available in the GitHub project.