如何在Spring Data中精美地更新JPA实体?

Luk*_*kor 14 java spring jpa spring-data spring-data-jpa

所以我查看了有关使用Spring Data的JPA的各种教程,这在很多场合都有所不同,我不太确定正确的方法是什么.

假设有以下实体:

package stackoverflowTest.dao;

import javax.persistence.*;

@Entity
@Table(name = "customers")
public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private long id;

@Column(name = "name")
private String name;

public Customer(String name) {
    this.name = name;
}

public Customer() {
}

public long getId() {
    return id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
}
Run Code Online (Sandbox Code Playgroud)

我们还有一个DTO,它在服务层中检索,然后交给控制器/客户端.

package stackoverflowTest.dto;

public class CustomerDto {

private long id;
private String name;

public CustomerDto(long id, String name) {
    this.id = id;
    this.name = name;
}

public long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
}
Run Code Online (Sandbox Code Playgroud)

所以现在假设客户想要在webui中更改他的名字 - 然后会有一些控制器操作,其中将有更新的DTO,其中包含旧ID和新名称.

现在我必须将这个更新的DTO保存到数据库中.

目前,目前无法更新现有客户(除了删除数据库中的条目并使用新的自动生成的ID创建新的Cusomter)

然而,因为这是不可行的(特别是考虑到这样的实体可能有数百个关系) - 所以我脑子里有两个直接的解决方案:

  1. 在Customer类中为id创建一个setter - 从而允许设置id,然后通过相应的存储库保存Customer对象.

要么

  1. 将id字段添加到构造函数中,每当您想要更新客户时,始终使用旧id创建一个新对象,但是其他字段的新值(在这种情况下只是名称)

所以我的问题是,有一般规则如何做到这一点?也许我解释的两种方法的缺点是什么?

Rob*_*roj 56

甚至更好的是@Tanjim Rahman回答你可以使用Spring Data JPA使用该方法 T getOne(ID id)

Customer customerToUpdate = customerRepository.getOne(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
Run Code Online (Sandbox Code Playgroud)

是更好的,因为getOne(ID id) 只获取一个引用(代理)对象,而不是从数据库中获取它.在这个引用上,您可以设置您想要的内容,并在save()其上只执行您期望的SQL UPDATE语句.find()比如在@Tanjim Rahmans中调用时,回答弹簧数据JPA将执行一个SQL SELECT,以便在您刚刚更新时从数据库中物理地获取实体,这是您不需要的.

  • GetOne 现已弃用 (8认同)
  • This isn't correct. The reference is used for lazy loading (or if reference is linked to an entity, it doesn't need to be loaded at all). It has nothing to do with lazily updating values. Unless someone can show a JPA implementation that works like this, there's no evidence of this happening, and it's not according to JPA spec (although I guess it doesn't expressly forbid it either). (5认同)
  • Robert Niestroj和Lukas Makor,我已经检查了您的答案。它肯定会比查找性能更好,因为不会从数据库读取数据。但这也有安全隐患。如果您不想更新Customer表的列怎么办。假设您要更新客户名称,但是您不想更新客户创建日期(因为它只会通过插入而不会更新)。在这些情况下,最好在更新之前获取数据库当前行。另外,如果要使用JPA版本进行乐观锁定,则可能会遇到问题。 (2认同)
  • 这将针对所有字段发出更新语句`update tbl set f1=v1, f2=v2, etc... where id=?` https://dba.stackexchange.com/q/176582/148282 (2认同)
  • 在我的测试案例中引发LazyInitializationException。 (2认同)
  • 错误答案如何设置“正确答案”?你有 id 字段和目标列+值吗?你为什么不发布更新? (2认同)
  • getOne() 被 getReferenceById() 取代 [Spring 文档 - JpaRepository](https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository. html#getOne-ID-) (2认同)

gor*_*est 7

在 Spring Data 中,如果您有 ID,您只需定义一个更新查询

  @Repository
  public interface CustomerRepository extends JpaRepository<Customer , Long> {

     @Query("update Customer c set c.name = :name WHERE c.id = :customerId")
     void setCustomerName(@Param("customerId") Long id, @Param("name") String name);

  }
Run Code Online (Sandbox Code Playgroud)

一些解决方案声称使用 Spring 数据并改为执行 JPA oldschool(即使以丢失更新的方式)。