是否应将实体 class 用作请求正文

Should entity class be used as request body

假设我必须保存一个实体,在本例中为 Book。我有下一个代码:

@RestController
@RequestMapping("books")
public class BookController {
    
    @Inject
    BookRepository bookRepository;
    
    @PostMapping
    public Book saveBook(@RequestBody Book book) {
        return bookRepository.save(book);
    }
}

我的实体 Book 是一个持久化实体:

@Entity(name = "BOOK")
public class Book{

    @Id
    @Column(name = "book_id")
    private Integer id;

    @Column(name = "title")
    private String title;

    (get/sets...)
}

问题是:在控制器层的 @RequestBody 中使用我的持久性实体是一种不好的做法吗?或者我应该创建一本书 DTO 并将其映射到服务层中的持久性 class?什么更好,为什么?

您应该创建一个 DTO class 并将其映射到持久性 class。参考 this rule description 同样的。指定的原因是

if a persistent object is used as an argument of a method annotated with @RequestMapping, it’s possible from a specially crafted user input, to change the content of unexpected fields into the database

除此之外,使用 DTO 我们可以省略一些我们不希望在表示层中成为 present/visible 的持久对象属性。

您可以将 DTO class 映射到控制器本身中的持久化实体,如下所示。

@RestController
@RequestMapping("books")
public class BookController {
    
    @Autowired
    BookRepository bookRepository;

    @Autowired
    ModelMapper modelMapper
    
    @PostMapping
    public Book saveBook(@RequestBody BookDTO modelBook) {
        Book book = this.modelMapper.map(modelBook, Book.class);
        return bookRepository.save(book);
    }
}

ModelMapper 是一个框架,可以执行 DTO 到实体的映射,反之亦然。检查 ModelMapper website.

您可以参考 answer and comments 了解更多相关信息。

是的,这是一个非常糟糕的主意。

实体表示在数据库中维护的持久数据,并封装了企业范围的业务规则。另一方面,DTO 是一个愚蠢的对象——它只包含属性和 getter 和 setter,但没有任何其他重要的逻辑。 DTO 仅用于将数据从应用程序的一个子系统传输到另一个子系统。

假设有一个新的需求来添加新的多对多关系:

@Entity(name = "BOOK")
public class Book{

    @Id
    @Column(name = "book_id")
    private Integer id;

    @ManyToMany
    Set<Student> likes;

... 
} 

数据库中的此类更改也会更改 API。