Spring Boot DTO Example - Entity To DTO Conversion

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

ModelMapper aims to make object mapping easy by automatically determining how one object model maps to another, based on conventions, in the same way, that a human would - while providing a simple, refactoring-safe API for handling specific use cases.

Read more about the model mapper library at http://modelmapper.org/.

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

Let's create a Post entity class and add the following content to it:
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

Let's create PostDto class and add the following content to it:
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 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: Repository Layer

Let's create a PostRepository to talk with the database for the Post entity:
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

In the service layer, we will only work with the Post entity, not PostDto class:

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;
	}
}
Note that we are not using an entity to DTO and vice versa logic in the service layer.

Step 6: Configure ModelMapper Class as a Spring Bean

Let's configure ModelMapper class as Spring bean so that we can inject it into the controller class:
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

In the below PostController class, we have injected ModelMapper class and used it in different REST APIs for an entity to DTO, vice versa conversion:
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);
	}
}

We have used ModelMapper in createPost() method to convert Entity to DTO and vice versa:
        @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);
	}

We have used ModelMapper in updatePost() method to convert Entity to DTO and vice versa:

	// 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);
	}

We have used ModelMapper in getPostById() method to convert Entity to DTO and vice versa:

	@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);
	}

We have used ModelMapper in getAllPosts() method to convert Entity to DTO and vice versa:

        @GetMapping
	public List<PostDto> getAllPosts() {

		return postService.getAllPosts().stream().map(post -> modelMapper.map(post, PostDto.class))
				.collect(Collectors.toList());
	}

Conclusion

This tutorial demonstrated how to do a conversion from Entity to DTO and from DTO to Entity in a Spring boot REST API project. We have used the model mapper library instead of writing these conversions by hand.

References

Comments

  1. hi, thank you for the tutorial
    I'm a begginer and I don't understand what did you put in PostRepository because you forget to put this class in your tutorial?

    ReplyDelete
    Replies
    1. Yes, PostRepository was missing. I have added it, thanks for informing.

      Delete
  2. Wen I want to update one field among many then how can we do that ?
    Modelmapper is not working.

    Also how can we do it using MongoDB ?

    ReplyDelete

Post a Comment

Leave Comment