问题描述
实体的身份由它们的主键定义。由于firstname
andlastname
不是主键的一部分,因此您不能告诉 JPA
将User
具有相同firstname
s 的 s 和lastname
具有不同 s 的 s 视为相等userId
。
因此,如果您想更新User
由其firstname
and标识的 alastname
,您需要User
通过查询找到它,然后更改您找到的对象的适当字段。这些更改将在事务结束时自动刷新到数据库,因此您无需执行任何操作即可显式保存这些更改。
也许我应该详细说明 JPA 的整体语义。设计持久性 API 有两种主要方法:
-
。当您需要修改数据库时,您应该显式调用持久性 API 的方法:调用
insert
以插入对象,或update
将对象的新状态保存到数据库。 -
。在这种情况下,您有一组由持久性库 管理的对象。 您对这些对象所做的所有更改都将在工作单元结束时自动刷新到数据库中(即典型情况下在当前事务结束时)。当您需要向数据库中插入新记录时,您将相应的对象设为 managed 。 托管 对象由它们的主键标识,因此如果您使用预定义的主键 管理 对象,它将与相同 id 的数据库记录相关联,并且该对象的状态将自动传播到该记录。
JPA 遵循后一种方法。save()
在 Spring Data 中,JPA 由merge()
普通 JPA 支持,因此它使您的实体按上述方式进行管理。 这意味着调用save()
具有预定义 id
的对象将更新相应的数据库记录而不是插入新记录,并解释了为什么save()
不调用create()
。
解决方法
好吧,这个问题几乎说明了一切。使用 JPARepository 如何更新实体?
JPARepository 只有一个 保存 方法,它并没有告诉我它实际上是创建还是更新。例如,我向数据库 User 中插入了一个简单的
Object,它具有三个字段firstname
:lastname
和age
:
@Entity
public class User {
private String firstname;
private String lastname;
//Setters and getters for age omitted,but they are the same as with firstname and lastname.
private int age;
@Column
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@Column
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
private long userId;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public long getUserId(){
return this.userId;
}
public void setUserId(long userId){
this.userId = userId;
}
}
然后我简单地调用save()
,此时它实际上是插入数据库:
User user1 = new User();
user1.setFirstname("john"); user1.setLastname("dew");
user1.setAge(16);
userService.saveUser(user1);// This call is actually using the JPARepository: userRepository.save(user);
到现在为止还挺好。现在我想更新这个用户,比如改变他的年龄。为此,我可以使用查询,无论是 QueryDSL 还是 NamedQuery。但是,考虑到我只想使用
spring-data-jpa 和 JPARepository,我该如何告诉它我想要更新而不是插入?
具体来说,我如何告诉 spring-data-jpa 具有相同用户名和名字的用户实际上是 EQUAL 并且应该更新现有实体?覆盖 equals
并没有解决这个问题。