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


当前回答

因为@axtavt的回答关注的是JPA而不是spring-data-jpa

通过查询来更新一个实体,然后保存是不高效的,因为它需要两次查询,而且可能查询会非常昂贵,因为它可能会连接其他表并加载任何fetchType= fetchType的集合。急切的

Spring-data-jpa支持更新操作。 您必须在Repository接口中定义该方法。并使用@Query和@Modifying进行注释。

@Modifying
@Query("update User u set u.firstname = ?1, u.lastname = ?2 where u.id = ?3")
void setUserInfoById(String firstname, String lastname, Integer userId);

@Query用于定义自定义查询,@Modifying用于告诉spring-data-jpa这个查询是一个更新操作,它需要executeUpdate()而不是executeQuery()。

您可以将返回类型指定为int,其中包含正在更新的记录的数量。


注意:在事务中运行此代码。

其他回答

如果你的主键是自动递增的,那么你必须设置主键的值。 对于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);
    }
  }

因为@axtavt的回答关注的是JPA而不是spring-data-jpa

通过查询来更新一个实体,然后保存是不高效的,因为它需要两次查询,而且可能查询会非常昂贵,因为它可能会连接其他表并加载任何fetchType= fetchType的集合。急切的

Spring-data-jpa支持更新操作。 您必须在Repository接口中定义该方法。并使用@Query和@Modifying进行注释。

@Modifying
@Query("update User u set u.firstname = ?1, u.lastname = ?2 where u.id = ?3")
void setUserInfoById(String firstname, String lastname, Integer userId);

@Query用于定义自定义查询,@Modifying用于告诉spring-data-jpa这个查询是一个更新操作,它需要executeUpdate()而不是executeQuery()。

您可以将返回类型指定为int,其中包含正在更新的记录的数量。


注意:在事务中运行此代码。

你可以简单地将这个函数与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);
}

Spring data save()方法将帮助您执行这两项:添加新项和更新现有项。

只需调用save(),享受生活:))

在java 8中,你可以在UserService中使用存储库的findById

@Service
public class UserServiceImpl {

    private final UserRepository repository;

    public UserServiceImpl(UserRepository repository) {
        this.repository = repository;
    }

    @Transactional
    public void update(User user) {
        repository
                .findById(user.getId()) // returns Optional<User>
                .ifPresent(user1 -> {
                    user1.setFirstname(user.getFirstname);
                    user1.setLastname(user.getLastname);

                    repository.save(user1);
                });
    }

}