这个问题几乎说明了一切。使用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,并且现有实体应该被更新?重写等号并不能解决这个问题。


当前回答

使用spring-data-jpa save()时,我遇到了与@DtechNet相同的问题。我的意思是每次save()都是创建新对象而不是更新。为了解决这个问题,我必须将版本字段添加到实体和相关表。

其他回答

你可以简单地将这个函数与save() jpfunction一起使用,但是作为参数发送的对象必须包含数据库中已有的id,否则它将无法工作,因为save()当我们发送一个没有id的对象时,它直接在数据库中添加一行,但如果我们发送一个具有现有id的对象,它将更改数据库中已经找到的列。

public void updateUser(Userinfos u) {
    User userFromDb = userRepository.findById(u.getid());
    // crush the variables of the object found
    userFromDb.setFirstname("john"); 
    userFromDb.setLastname("dew");
    userFromDb.setAge(16);
    userRepository.save(userFromDb);
}

你可以看到下面的例子:

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(实体)方法插入一条新记录。

使用@DynamicUpdate注释。它更简洁,您不必为了获取保存的值而查询数据库。

我是这样解决这个问题的:

User inbound = ...
User existing = userRepository.findByFirstname(inbound.getFirstname());
if(existing != null) inbound.setId(existing.getId());
userRepository.save(inbound);