Spring JPA 发布 ManyToOne 父实体

Spring JPA posting ManyToOne parent entity

提问人:Gambanishu Habbeba 提问时间:2/28/2022 更新时间:2/28/2022 访问量:297

问:

情况是这样的:对象,用户可以做的一些工作。用户还可以在作品上签字。所以一个非常基本的工作对象: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

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),我也知道这个用户角色。现在,有一个控制器可以检索一个或多个工作对象,自然而然地,这些对象的返回类型是 或 。必须有一个处理发布新实体的功能。我在某处读到过,在 REST API 中,如果往返的实体相同,那就太好了。那么,一个人应该发送什么结构来发布作品呢?与查询时得到的相同。然后:WorkList<Work>Work

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

这非常方便,我真的很喜欢,只是它根本不起作用。现在,请求正文必须是一个有效的对象,该对象听起来很甜蜜,验证时间类似于毫秒,但事实并非如此。它需要 2 个对象,一个位于 signTargetUser,一个位于 signedUser。更糟糕的是,这些用户必须挂有对象。更糟糕的是,对象必须具有非 null 密码属性。显然我什至不知道,这现在已经失控了。我想做的就是插入一个对象,知道signTargetUserId(如果有的话)。 我看到一个分 2 个步骤的解决方案,但我一点也不喜欢它:WorkUserRoleUserWork

  1. 首先,我需要创建另一个类 () 来描述帖子中传入的对象的结构,但这次没有 ManyToOne 关系。但是当你回读你创作的作品时,你会得到一个不同的结构。 仔细地。更不用说半无用的新类了,它主要是第一个类的糟糕重复。如果我添加或更改字段,是否需要更改 2 个文件?认真地?WorkIncomingWorkIncomingWork
  2. 显然,原始存储库正在处理类,因此它将无法处理 type: new repository。再次,完全代码重复,维护等等。我现在甚至不确定 JPA 对引用同一表的 2 个实体会有什么感觉。WorkWorkIncoming

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

Java Spring JPA

评论


答:

0赞 Drashti Dobariya 2/28/2022 #1

最常见的方法是将 DTO 对象用于请求和响应。 应使用包含所有工作字段的 DTO 名称 - workId、type、flags 等以及 signTargetUserId。DTO 字段名称应与实体的名称相同,以便于映射。

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

之后,使用 signTargetUserId 从 userRepository 获取用户 Object。

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:这只是编写服务的方法之一。您可能会发现还有其他一些方法更符合您的编码风格。

评论

0赞 Gambanishu Habbeba 2/28/2022
明白了!因此,这些物体实际上从来不会进出整个事物,它总是它的按摩版本:物体。因此,具有其关系的实体仅适用于控制器级别,仅供我们使用。发送作为实际实体的实际对象是一种懒惰的捷径,一开始就不应该发生。这就是为什么我一直对此有疑问,因为我总是试图发送和接收实际的实体。多么尴尬......WorkWorkDTOWork