In this tutorial, we will learn how to create a DTOs (Data Transfer Objects) class in the spring boot application and how to convert Entities to DTOs and vice versa using the ModelMapper library.
Data Transfer Object Design Pattern is a frequently used design pattern. It is basically used to pass data with multiple attributes in one shot from client to server, to avoid multiple calls to a remote server.
Another advantage of using DTOs on RESTful APIs written in Java (and on Spring Boot), is that they can help to hide implementation details of domain objects (JPA entities). Exposing entities through endpoints can become a security issue if we do not carefully handle what properties can be changed through what operations.
Let's start with introducing the ModelMapper Java library that we are going to use to convert Entity to DTO and vice versa.
ModelMapper Library
We will need this dependency in the pom.xml:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.5</version>
</dependency>
Step 1: Add ModelMapper Library to Pom.xml
We will need this dependency in the pom.xml:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.5</version>
</dependency>
Step 2: Define JPA Entity - Post.java
package net.javaguides.springboot.model; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "posts", uniqueConstraints = {@UniqueConstraint(columnNames = {"title"})}) public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "title") private String title; @Column(name = "description") private String description; @Column(name = "content") private String content; }
Step 3: Define DTO Class - PostDto.java
package net.javaguides.springboot.payload; import java.util.HashSet; import java.util.Set; import lombok.Data; @Data public class PostDto { private long id; private String title; private String description; private String content; }Include only those details in the DTO class which is required for the client. The Entity and DTO fields look the same but make that you will add fields that are required to the client.
Step 4: Service Layer
PostService interface
package net.javaguides.springboot.service; import java.util.List; import net.javaguides.springboot.model.Post; public interface PostService { List<Post> getAllPosts(); Post createPost(Post post); Post updatePost(long id, Post post); void deletePost(long id); Post getPostById(long id); }
PostServiceImpl Class
package net.javaguides.springboot.service.impl; import java.util.List; import java.util.Optional; import org.springframework.stereotype.Service; import net.javaguides.springboot.exception.ResourceNotFoundException; import net.javaguides.springboot.model.Post; import net.javaguides.springboot.repository.PostResository; import net.javaguides.springboot.service.PostService; @Service public class PostServiceImpl implements PostService{ private final PostResository postRepository; public PostServiceImpl(PostResository postRepository) { super(); this.postRepository = postRepository; } @Override public List<Post> getAllPosts() { return postRepository.findAll(); } @Override public Post createPost(Post post) { return postRepository.save(post); } @Override public Post updatePost(long id, Post postRequest) { Post post = postRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Post", "id", id)); post.setTitle(postRequest.getTitle()); post.setDescription(postRequest.getDescription()); post.setContent(postRequest.getContent()); return postRepository.save(post); } @Override public void deletePost(long id) { Post post = postRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Post", "id", id)); postRepository.delete(post); } @Override public Post getPostById(long id) { Optional<Post> result = postRepository.findById(id); if(result.isPresent()) { return result.get(); }else { throw new ResourceNotFoundException("Post", "id", id); } // Post post = postRepository.findById(id) // .orElseThrow(() -> new ResourceNotFoundException("Post", "id", id)); //return post; } }
Step 5: Configure ModelMapper Class a Spring Bean
package net.javaguides.springboot; import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class SpringbootBlogApiApplication { @Bean public ModelMapper modelMapper() { return new ModelMapper(); } public static void main(String[] args) { SpringApplication.run(SpringbootBlogApiApplication.class, args); } }
Step 6: Controller Layer
package net.javaguides.springboot.contoller; import java.util.List; import java.util.stream.Collectors; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import net.javaguides.springboot.model.Post; import net.javaguides.springboot.payload.ApiResponse; import net.javaguides.springboot.payload.PostDto; import net.javaguides.springboot.service.PostService; @RestController @RequestMapping("/api/posts") public class PostController { @Autowired private ModelMapper modelMapper; private PostService postService; public PostController(PostService postService) { super(); this.postService = postService; } @GetMapping public List<PostDto> getAllPosts() { return postService.getAllPosts().stream().map(post -> modelMapper.map(post, PostDto.class)) .collect(Collectors.toList()); } @GetMapping("/{id}") public ResponseEntity<PostDto> getPostById(@PathVariable(name = "id") Long id) { Post post = postService.getPostById(id); // convert entity to DTO PostDto postResponse = modelMapper.map(post, PostDto.class); return ResponseEntity.ok().body(postResponse); } @PostMapping public ResponseEntity<PostDto> createPost(@RequestBody PostDto postDto) { // convert DTO to entity Post postRequest = modelMapper.map(postDto, Post.class); Post post = postService.createPost(postRequest); // convert entity to DTO PostDto postResponse = modelMapper.map(post, PostDto.class); return new ResponseEntity<PostDto>(postResponse, HttpStatus.CREATED); } // change the request for DTO // change the response for DTO @PutMapping("/{id}") public ResponseEntity<PostDto> updatePost(@PathVariable long id, @RequestBody PostDto postDto) { // convert DTO to Entity Post postRequest = modelMapper.map(postDto, Post.class); Post post = postService.updatePost(id, postRequest); // entity to DTO PostDto postResponse = modelMapper.map(post, PostDto.class); return ResponseEntity.ok().body(postResponse); } @DeleteMapping("/{id}") public ResponseEntity<ApiResponse> deletePost(@PathVariable(name = "id") Long id) { postService.deletePost(id); ApiResponse apiResponse = new ApiResponse(Boolean.TRUE, "Post deleted successfully", HttpStatus.OK); return new ResponseEntity<ApiResponse>(apiResponse, HttpStatus.OK); } }
@PostMapping public ResponseEntity<PostDto> createPost(@RequestBody PostDto postDto) { // convert DTO to entity Post postRequest = modelMapper.map(postDto, Post.class); Post post = postService.createPost(postRequest); // convert entity to DTO PostDto postResponse = modelMapper.map(post, PostDto.class); return new ResponseEntity<PostDto>(postResponse, HttpStatus.CREATED); }
// change the request for DTO // change the response for DTO @PutMapping("/{id}") public ResponseEntity<PostDto> updatePost(@PathVariable long id, @RequestBody PostDto postDto) { // convert DTO to Entity Post postRequest = modelMapper.map(postDto, Post.class); Post post = postService.updatePost(id, postRequest); // entity to DTO PostDto postResponse = modelMapper.map(post, PostDto.class); return ResponseEntity.ok().body(postResponse); }
@GetMapping("/{id}") public ResponseEntity<PostDto> getPostById(@PathVariable(name = "id") Long id) { Post post = postService.getPostById(id); // convert entity to DTO PostDto postResponse = modelMapper.map(post, PostDto.class); return ResponseEntity.ok().body(postResponse); }
@GetMapping public List<PostDto> getAllPosts() { return postService.getAllPosts().stream().map(post -> modelMapper.map(post, PostDto.class)) .collect(Collectors.toList()); }
Comments
Post a comment