我正在使用 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;
}
请问最好的方法是什么?
答案 0 :(得分:3)
那是因为当您发送该子信息的请求时
{ "username": "philippe",
"userId": 2,
"email": "philipppe@gmail.com"
}
您的孩子被视为休眠的新实体。您的密钥在这里为空。它无法知道它是否重复。
第一个解决方案
不要发送这样的更新信息。首先从您的 Api 获取信息,进行更改并将其发回更新。这样你的孩子就会有这个 ID。
所以客户端点击你的 api 来检索用户 1 并收回
userRepository.findById(1L);
客户端对用户进行更改
客户端调用 API 来更新调用的用户
userRepository.save(user)
但现在用户将拥有孩子
{ "id": 1
"username": "philippe",
"userId": 2,
"email": "philipppe@gmail.com"
}
第二种解决方案
更改密钥,以便您的孩子身份不是基于某个数据库长值,而是基于某个业务密钥。 例如,您可以将 userId with username 标记为复合键
这样,即使在将某些内容持久化到数据库中之前,密钥也将始终存在。因此,hibernate 甚至在持久化之前就会知道何时重复或不重复。
这是一篇关于您面临的问题的非常受欢迎的文章
Don't let hibernate steal your identity
第三种解决方案
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name = "owner_id", nullable = false)
private Set<Friend> friends = new HashSet<>();
从用户中删除 cascade = CascadeType.ALL
。
然后让该 API 调用中的客户端仅更新用户字段,而不是与其子项有关的内容。 如果客户端想要更新某个好友,他必须像现在为用户所做的那样,为该特定好友调用相关 API。
用户更新调用中包含的任何朋友信息都将被忽略。
答案 1 :(得分:1)
因为您的客户端没有发送 friendId
和类 Friend
@Entity
@Table(name = "friends")
public class Friend {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
所以你不知道什么朋友需要更新,friendId
是自动递增的所以你得到了重复,让我们像这样更新请求:
{
"username": "bertrand",
"email": "bertrand@gmail.com",
"birthdate": "11/05/1984",
"lang": "fr",
"secretKey": "qefieqjnhfoipqnhefipqnfp",
"updatedOn": "mise a jour date",
"createdOn": "creation date",
"friends": [
{
"id": 1,
"username": "philippe",
"userId": 2,
"email": "philipppe@gmail.com"
}
]
}