这个问题几乎说明了一切。使用JPARepository我如何更新一个实体?
JPARepository只有一个save方法,它并没有告诉我它是创建还是更新。例如,我插入一个简单的对象到数据库User,它有三个字段:姓,名和年龄:
@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,并且现有实体应该被更新?重写等号并不能解决这个问题。
你可以看到下面的例子:
private void updateDeliveryStatusOfEvent(Integer eventId, int deliveryStatus) {
try {
LOGGER.info("NOTIFICATION_EVENT updating with event id:{}", eventId);
Optional<Event> eventOptional = eventRepository.findById(eventId);
if (!eventOptional.isPresent()) {
LOGGER.info("Didn't find any updatable notification event with this eventId:{}", eventId);
}
Event event = eventOptional.get();
event.setDeliveryStatus(deliveryStatus);
event = eventRepository.save(event);
if (!Objects.isNull(event)) {
LOGGER.info("NOTIFICATION_EVENT Successfully Updated with this id:{}", eventId);
}
} catch (Exception e) {
LOGGER.error("Error :{} while updating NOTIFICATION_EVENT of event Id:{}", e, eventId);
}
}
或使用本机查询更新:
public interface YourRepositoryName extends JpaRepository<Event,Integer>{
@Transactional
@Modifying
@Query(value="update Event u set u.deliveryStatus = :deliveryStatus where u.eventId = :eventId", nativeQuery = true)
void setUserInfoById(@Param("deliveryStatus")String deliveryStatus, @Param("eventId")Integer eventId);
}
正如其他人已经提到的,save()本身包含创建和更新操作。
我只是想补充一下save()方法后面的内容。
首先,让我们看看CrudRepository<T,ID>,
好的,让我们检查在SimpleJpaRepository<T, ID>,
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
如你所见,它将首先检查ID是否存在,如果实体已经存在,则仅通过merge(实体)方法进行更新,否则,则通过persist(实体)方法插入一条新记录。
实体的标识由它们的主键定义。因为姓和名不是主键的一部分,所以您不能告诉JPA,如果用户名和姓不同,就把它们当作相同的用户来对待。
因此,如果您想要更新一个由其姓和名标识的User,您需要通过查询找到该User,然后更改所找到的对象的适当字段。这些更改将在事务结束时自动刷新到数据库,因此您不需要执行任何操作来显式保存这些更改。
编辑:
也许我应该详细说明JPA的整体语义。持久性api的设计主要有两种方法:
insert/update approach. When you need to modify the database you should call methods of persistence API explicitly: you call insert to insert an object, or update to save new state of the object to the database.
Unit of Work approach. In this case you have a set of objects managed by persistence library. All changes you make to these objects will be flushed to the database automatically at the end of Unit of Work (i.e. at the end of the current transaction in typical case). When you need to insert new record to the database, you make the corresponding object managed. Managed objects are identified by their primary keys, so that if you make an object with predefined primary key managed, it will be associated with the database record of the same id, and state of this object will be propagated to that record automatically.
JPA遵循后一种方法。Spring Data JPA中的save()是由普通JPA中的merge()支持的,因此它使您的实体像上面描述的那样管理。这意味着在具有预定义id的对象上调用save()将更新相应的数据库记录,而不是插入新的记录,这也解释了为什么save()不调用create()。
如果你的主键是自动递增的,那么你必须设置主键的值。
对于save();方法作为update()工作。否则它将在db中创建一个新记录。
如果使用JSP表单,则使用隐藏字段设置主键。
Jsp:
<form:input type="hidden" path="id" value="${user.id}"/>
Java:
@PostMapping("/update")
public String updateUser(@ModelAttribute User user) {
repo.save(user);
return "redirect:userlist";
}
再看看这个:
@Override
@Transactional
public Customer save(Customer customer) {
// Is new?
if (customer.getId() == null) {
em.persist(customer);
return customer;
} else {
return em.merge(customer);
}
}
你可以看到下面的例子:
private void updateDeliveryStatusOfEvent(Integer eventId, int deliveryStatus) {
try {
LOGGER.info("NOTIFICATION_EVENT updating with event id:{}", eventId);
Optional<Event> eventOptional = eventRepository.findById(eventId);
if (!eventOptional.isPresent()) {
LOGGER.info("Didn't find any updatable notification event with this eventId:{}", eventId);
}
Event event = eventOptional.get();
event.setDeliveryStatus(deliveryStatus);
event = eventRepository.save(event);
if (!Objects.isNull(event)) {
LOGGER.info("NOTIFICATION_EVENT Successfully Updated with this id:{}", eventId);
}
} catch (Exception e) {
LOGGER.error("Error :{} while updating NOTIFICATION_EVENT of event Id:{}", e, eventId);
}
}
或使用本机查询更新:
public interface YourRepositoryName extends JpaRepository<Event,Integer>{
@Transactional
@Modifying
@Query(value="update Event u set u.deliveryStatus = :deliveryStatus where u.eventId = :eventId", nativeQuery = true)
void setUserInfoById(@Param("deliveryStatus")String deliveryStatus, @Param("eventId")Integer eventId);
}