java - 多次更新父项时,JPA 在子项上重复条目
问题描述
我正在使用 Java、Spring Boot、Hibernate 和 JPA 构建 Rest API。我有 2 个表用户和朋友,其中用户将有一个朋友数组。我的客户将在我的用户表上发送几个更新请求。这是 API 将收到的请求:
{
"username": "bertrand",
"email": "bertrand@gmail.com",
"birthdate": "11/05/1984",
"lang": "fr",
"secretKey": "qefieqjnhfoipqnhefipqnfp",
"updatedOn": "mise a jour date",
"createdOn": "creation date",
"friends": [
{
"username": "philippe",
"userId": 2,
"email": "philipppe@gmail.com"
}
]
}
如果我的客户发送此请求,我的 API 会返回:
{
"id": 1,
"username": "bertrand",
"password": "$2a$10$zGpK7/SOceA1xZnphrItpuYRkViBa7pNNgt9DsKDH1Q7HY50FE9hm",
"email": "bertrandpetit10@gmail.com",
"birthdate": "11 mai 1984",
"lang": "fr",
"secretKey": "8138124aff2b9226",
"updatedOn": "mise a jour date",
"createdOn": "creation date",
"friends": [
{
"id": 1,
"userId": 2,
"username": "philippe",
"email": "philipppe@gmail.com"
}
]
}
这个答案很好,但是如果我的客户再次发送相同的请求,朋友数组中会填充一个重复的朋友。
{
"id": 1,
"username": "bertrand",
"password": "$2a$10$zGpK7/SOceA1xZnphrItpuYRkViBa7pNNgt9DsKDH1Q7HY50FE9hm",
"email": "bertrandpetit10@gmail.com",
"birthdate": "11 mai 1984",
"lang": "fr",
"secretKey": "8138124aff2b9226",
"updatedOn": "mise a jour date",
"createdOn": "creation date",
"friends": [
{
"id": 1,
"userId": 2,
"username": "philippe",
"email": "philipppe@gmail.com"
},
{
"id": 2,
"userId": 2,
"username": "philippe",
"email": "philipppe@gmail.com"
}
]
}
我希望每个朋友都是独一无二的,如果已经存在,不要在 Friend 表中创建新条目。
这是我的用户实体:
Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "username", nullable = false, length = 20, unique = true)
private String username;
@Column(name = "password", nullable = false, length = 100)
private String password;
@Column(name = "email", nullable = false, length = 50, unique = true)
private String email;
@Column(name = "birthdate", nullable = false, length = 100)
private String birthdate;
@Column(name = "language", nullable = false, length = 2)
private String lang;
@Column(name = "secret_key", nullable = false, length = 200)
private String secretKey;
@Column(name = "updated_on", nullable = false, length = 100)
private String updatedOn;
@Column(name = "created_on", nullable = false, length = 100)
private String createdOn;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "owner_id", nullable = false)
private Set<Friend> friends = new HashSet<>();
public User() {
}
public User(String username, String email, String birthdate, String lang, String secretKey, String updatedOn,
String createdOn, Set<Friend> friends, String password) {
this.username = username;
this.email = email;
this.birthdate = birthdate;
this.lang = lang;
this.secretKey = secretKey;
this.updatedOn = updatedOn;
this.createdOn = createdOn;
this.friends = friends;
this.password = password;
}
+ getters ans setters...
这是我的朋友实体:
@Entity
@Table(name = "friends")
public class Friend {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "user_id", nullable = false, length = 20)
private long userId;
@Column(name = "username", nullable = false, length = 20)
private String username;
@Column(name = "email", nullable = false, length = 50)
private String email;
public Friend(long id, long userId, String username, String email) {
this.id = id;
this.userId = userId;
this.username = username;
this.email = email;
}
请问最好的方法是什么?
解决方案
那是因为当您发送对该子信息的请求时
{ "username": "philippe",
"userId": 2,
"email": "philipppe@gmail.com"
}
您的孩子被认为是休眠的新实体。您的密钥在此处为空。它无法知道它是否重复。
第一种解决方案
不要发送这样的更新信息。首先从您的 Api 获取信息,进行更改并将其发回以进行更新。这样你的孩子就会有身份证。
所以客户端点击你的api来检索用户1并收回
userRepository.findById(1L);
客户对用户进行更改
客户端调用 API 来更新调用的用户
userRepository.save(user)
但现在用户将拥有孩子
{ "id": 1
"username": "philippe",
"userId": 2,
"email": "philipppe@gmail.com"
}
第二种解决方案
更改密钥,使您的孩子的身份不是基于某些数据库长值,而是基于某些业务密钥。例如,您可以使用用户名将 userId标记为复合键
这样,即使在数据库中持久化某些内容之前,密钥也将始终存在。因此,hibernate 甚至在它被持久化之前就会知道什么时候是重复的。
这是一篇关于您面临的问题的非常受欢迎的文章
第三个解决方案
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "owner_id", nullable = false)
private Set<Friend> friends = new HashSet<>();
cascade = CascadeType.ALL
从用户中删除。
然后让该 API 调用的客户端仅更新用户字段,而不是与其子项有关的内容。如果客户想要更新某个朋友,他必须为该特定朋友调用相关的 API,就像现在为用户所做的那样。
用户更新调用中包含的任何好友信息都将被忽略。
推荐阅读
- asp.net-core - 在 VS2019 上调试 docker-compose 时出错
- c# - DirectoryInfo.EnumerateDirectories() 在管理员模式下运行时不起作用
- sql-server - 如何在 SQL Server 2014 中呈现分层数据
- javascript - 根据屏幕大小动态更改表格行Vuejs
- python - 如果它们是零,则仅删除第一个条目
- spring-boot - Springboot 通过 application.properties 禁用 Apache Jserv 协议(AJP)
- node.js - 错误 [ERR_UNHANDLED_ERROR]:未处理的错误。(“不正确的论点”)
- python - Django:新值已添加到 mysql 表中,但在服务器重新启动之前未显示在 html 模板中
- javascript - 带有已保存复选框状态的事件冒泡
- java - 三星 10 plus 不允许 wifi p2p 发现