Spring JPA 发布 ManyToOne 父实体

Spring JPA posting ManyToOne parent entity

情况如下:Work对象,用户可以做的一些工作。用户还可以在作品上签字。所以一个非常基本的工作对象:

class Work (
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var workId: Int,
    var userId: Int,
    ...
    var flags: Int,
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "signTargetUserId")
    var signTargetUser: User?,

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "signedUserId")
    var signedUser: User?
)

User 有一些东西,这个那个...:[=​​29=]

class User(
    @Id
    val userId: Int,
    val username: String,
    val name: String,
    val email: String,
    @JsonIgnore
    val password: String,
    val flags: Int,

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "roleId")
    var role: Role
)

告诉 JPA 用户角色关系非常方便,我希望我们同意这一点。当我得到用户时,我也得到了角色。工友关系也很好,喜欢。我得到一个工作对象,我知道应该由谁在上面签字 (signTargetUser),而且我也知道这个用户角色。现在,有一个控制器可以检索一个或多个工作对象,自然 return 类型的这些是 WorkList<Work>。必须有一个处理 posting 新 Work 实体的函数。我在某个地方读过它,在 REST API 中,如果往返的实体相同,那就太好了。那么送post一部作品有什么结构呢?与查询时得到的相同。那那:

    fun createWork(@RequestBody work: Work, authentication: Authentication): ResponseEntity<Any> {

这非常方便,我真的很喜欢它,只是它根本不起作用。现在,请求正文必须是一个有效的 Work 对象,它在 ms 的验证中听起来很不错,但事实并非如此。它需要 2 个 User 个对象,一个在 signTargetUser,一个在 signedUser。更糟糕的是,这些用户身上必须挂着 Role 件物品。更糟糕的是,User 对象必须有一个非空密码 属性。显然我什至不知道,这现在已经失控了。我想要做的就是插入一个 Work 对象,知道 signTargetUserId(如果有的话)。 我看到一个分两步的解决方案,但我一点都不喜欢:

  1. 首先我需要创建另一个 class (WorkIncoming) 来描述进入 post 的对象的结构,但这次没有 ManyToOne 关系。但是当你回顾你创作的作品时,你会得到一个不同的结构。 WorkIncoming 进,Work 出。更不用说半无用的新 class ,它主要是第一个的糟糕重复。如果我添加或更改一个字段,是否需要更改 2 个文件?认真的吗?
  2. 显然原始存储库正在处理 Work classes,因此它将无法处理 WorkIncoming type: new repository then。再一次,总的代码重复,维护等等。我现在甚至不确定 JPA 对引用相同 table.
  3. 的 2 个实体有何看法

那么真正的解决方案是什么?真实的人是怎么做到的?我在这里没有想法。我刚才描述的这个解决方案技术含量非常低,不可能!

最常见的方法是使用 DTO 对象进行请求和响应。 应该使用包含所有工作字段的 DTO 名称 - 工作 ID、类型、标志等以及 signTargetUserId。 DTO字段名称应与实体名称相同,以便于映射。

class WorkDTO (
    var userId: Int,
    ...
    var flags: Int,
    var signTargetUserId: Int
)

之后,您使用 signTargetUserId 从 userRepository 中获取用户对象。

User user = this.usersRepository.findById(signTargetUserId);

这样,您就不需要 Role 对象,因为它已经存在于用户对象中。

然后在您的服务中,您可以使用许多映射器库,如 ModelMapper、MapStruct、JMapper、Orika、Dozer 等将您的 DTO 映射到工作实体。 记得将上面创建的用户对象也传递给映射器

只是一个例子(这个例子是模型映射器,但你可以使用你选择的映射器):

public Work convertDtoToEntity( WorkDTO workDto, User user) {

        this.modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        Work work = modelMapper.map(workDto, Work.class);
        work.setSignTargetUser(user);
        return work;
}

那就直接用workRepository的save方法吧

另外,回答你的第二个问题,永远不要为 DTO 创建存储库 类。

PS:这只是编写服务的一种方式。您可能会发现其他一些方法更符合您的编码风格。