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.
Learn more about the DTO pattern at Understanding Data Transfer Object Design Pattern
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 will use to convert Entity to DTO and vice versa.
YouTube Video - Spring Boot DTO Example
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 jakarta.persistence.*;
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;
}
Step 4: Repository Layer
package com.springboot.blog.repository;
import com.springboot.blog.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostRepository extends JpaRepository<Post, Long> {
}
Step 5: 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 6: Configure ModelMapper Class as 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 7: 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()); }
hi, thank you for the tutorial
ReplyDeleteI'm a begginer and I don't understand what did you put in PostRepository because you forget to put this class in your tutorial?
Yes, PostRepository was missing. I have added it, thanks for informing.
DeleteWen I want to update one field among many then how can we do that ?
ReplyDeleteModelmapper is not working.
Also how can we do it using MongoDB ?