在本教程中,我们将学习如何在Spring Boot 应用程序中创建 DTO(数据传输对象)类,以及如何使用 ModelMapper 库将实体转换为 DTO,反之亦然。

数据传输对象设计模式是一种常用的设计模式。它基本上用于一次性将具有多个属性的数据从客户端传递到服务器,以避免多次调用远程服务器。

在用Java编写的RESTful API上使用DTO(以及在Spring Boot上)的另一个优点是,它们可以帮助隐藏域对象(JPA实体)的实现细节。如果我们不仔细处理可以通过哪些操作更改哪些属性,则通过终结点公开实体可能会成为安全问题。

让我们从介绍ModelMapper Java库开始,我们将使用它来将实体转换为DTO,反之亦然。

模型映射器库

ModelMapper 旨在通过根据约定自动确定一个对象模型如何映射到另一个对象模型,使对象映射变得容易,就像人类一样,同时提供一个简单的、重构安全的 API 来处理特定的用例。
ModelMapper - Simple, Intelligent, Object Mapping. 阅读有关模型映射器库的更多信息。

我们将在pom中需要这种依赖.xml:

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.3.5</version>
</dependency>

第 1 步:将模型映射器库添加到 Pom.xml

我们将在pom中需要这种依赖.xml:

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.3.5</version>
</dependency>

步骤 2:定义 JPA 实体 - Post.java

让我们创建一个Post实体类并向其添加以下内容:
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;
}

步骤 3:定义 DTO 类 - PostDto.java

让我们创建PostDto类并向其添加以下内容:
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;
}

仅包含客户端所需的 DTO 类中的那些详细信息。实体和 DTO 字段看起来相同,但您将向客户端添加所需的字段。

步骤 4:存储库层

让我们创建一个PostRepository来与Post实体的数据库进行通信:
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> {

}

第 5 步:服务层

在服务层中,我们将只使用Post实体,而不使用PostDto类:

PostService 接口

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;
	}
}
请注意,我们不是在服务层中使用实体到 DTO,反之亦然。

步骤 6:将 ModelMapper 类配置为 Spring Bean

让我们将ModelMapper类配置为 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);
	}

}

步骤 7:控制器层

在下面的PostController类中,我们注入了ModelMapper类,并在不同的REST API中将其用于实体到DTO的转换,反之亦然:
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);
	}
}
我们在createPost()方法中使用ModelMapper将实体转换为DTO,反之亦然:
        @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);
	}
我们在更新Post() 方法中使用 ModelMapper 将实体转换为 DTO,反之亦然:
	// 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);
	}
我们在 getPostById() 方法中使用 ModelMapper 将实体转换为 DTO,反之亦然:
	@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);
	}
我们在getAllPosts()方法中使用ModelMapper将实体转换为DTO,反之亦然:
        @GetMapping
	public List<PostDto> getAllPosts() {

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

结论

本教程演示了如何在 Spring 引导 REST API 项目中执行从实体到 DTO 以及从 DTO 到实体的转换。我们使用了模型映射器库,而不是手动编写这些转换。

引用

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐