提问人:Gambanishu Habbeba 提问时间:2/28/2022 更新时间:2/28/2022 访问量:297
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
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 中,如果往返的实体相同,那就太好了。那么,一个人应该发送什么结构来发布作品呢?与查询时得到的相同。然后:Work
List<Work>
Work
fun createWork(@RequestBody work: Work, authentication: Authentication): ResponseEntity<Any> {
这非常方便,我真的很喜欢,只是它根本不起作用。现在,请求正文必须是一个有效的对象,该对象听起来很甜蜜,验证时间类似于毫秒,但事实并非如此。它需要 2 个对象,一个位于 signTargetUser,一个位于 signedUser。更糟糕的是,这些用户必须挂有对象。更糟糕的是,对象必须具有非 null 密码属性。显然我什至不知道,这现在已经失控了。我想做的就是插入一个对象,知道signTargetUserId(如果有的话)。
我看到一个分 2 个步骤的解决方案,但我一点也不喜欢它:Work
User
Role
User
Work
- 首先,我需要创建另一个类 () 来描述帖子中传入的对象的结构,但这次没有 ManyToOne 关系。但是当你回读你创作的作品时,你会得到一个不同的结构。 仔细地。更不用说半无用的新类了,它主要是第一个类的糟糕重复。如果我添加或更改字段,是否需要更改 2 个文件?认真地?
WorkIncoming
WorkIncoming
Work
- 显然,原始存储库正在处理类,因此它将无法处理 type: new repository。再次,完全代码重复,维护等等。我现在甚至不确定 JPA 对引用同一表的 2 个实体会有什么感觉。
Work
WorkIncoming
那么真正的解决方案是什么呢?真实的人是怎么做到的?我在这里没有想法。我刚才描述的这个解决方案技术含量非常低,不可能!
答:
最常见的方法是将 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:这只是编写服务的方法之一。您可能会发现还有其他一些方法更符合您的编码风格。
评论
Work
WorkDTO
Work
评论