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

时间:2016-09-28 07:44:59

标签: 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;
}
}

我们还有一个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;
}
}

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

现在我必须将此更新的DTO保存到数据库中。

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

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

  1. 为Customer类中的id创建一个setter - 从而允许设置id,然后通过相应的存储库保存Customer对象。
    1. 将id字段添加到构造函数中,每当您想要更新客户时,始终使用旧id创建一个新对象,但是其他字段的新值(在这种情况下只是名称)
    2. 所以我的问题是,有一般规则如何做到这一点? 任何可能我解释的2种方法的缺点是什么?

7 个答案:

答案 0 :(得分:42)

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




  Customer customerToUpdate = customerRepository.getOne(id);
 customerToUpdate.setName(customerDto.getName);
 customerRepository.save(customerToUpdate);
  




更好,因为 getOne(ID id) 只获取一个引用(代理)对象,并且不从DB中获取它。在这个引用上你可以设置你想要的东西,在 save()它只会像你期望的那样执行SQL UPDATE语句。比较当你在@Tanjim中调用 find()时,Rahmans会回答弹簧数据,当你刚刚更新时,JPA会做一个SQL SELECT来从数据库中物理地获取你不需要的实体。 / p>


答案 1 :(得分:3)

简单的JPA更新..

Customer customer = em.find(id, Customer.class); //Consider em as JPA EntityManager
customer.setName(customerDto.getName);
em.merge(customer);

答案 2 :(得分:1)

这是一个对象初始化问题,而不是一个jpa问题,两种方法都可以工作,你可以同时拥有它们,通常如果数据成员值在实例化之前就已经准备好了你使用构造函数参数,如果这样的话在实例化后你应该有一个setter来更新value。

答案 3 :(得分:1)

如果您需要直接使用DTO而不是实体,则应检索现有客户实例,并将更新后的字段从DTO映射到该实例。

Customer entity = //load from DB
//map fields from DTO to entity

答案 4 :(得分:1)

在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);

  }

某些解决方案声称使用Spring数据并改用JPA(即使更新丢失的方式)。

答案 5 :(得分:0)

  

因此,现在假设客户要在网络用户界面中更改其姓名-   然后会有一些控制器动作,   用旧的ID和新的名称更新了DTO。

通常,您具有以下工作流程:

  1. 用户从服务器请求其数据并在UI中获取它们;
  2. 用户更正其数据,然后使用已经存在的ID将其发送回服务器;
  3. 在服务器上,您通过用户获取具有更新数据的DTO,并通过ID在DB中找到它(否则抛出异常),并转换DTO->具有所有给定数据,外键等的实体...
  4. 然后您只需合并,或者如果使用Spring Data调用save(),save()就会合并(请参阅thread); < / li>

PS 。此操作将不可避免地发出两个查询:选择并更新。同样,即使您要更新一个字段,也要进行2个查询。但是,如果您在实体类的顶部使用Hibernate专有的@DynamicUpdate批注,它将帮助您不在更新语句中包括所有字段,而仅包括实际更改的字段。

P.S。。如果您不想为第一次选择语句付费,而喜欢使用Spring Data的@Modifying查询,请准备好丢失与可修改实体相关的L2C缓存区域;使用本机更新查询的情况更糟(请参阅此thread),当然也准备好手动编写这些查询,对其进行测试并在将来提供支持。

答案 6 :(得分:0)

我遇到了这个问题!
幸运的是,我确定了 2种方式 并了解了一些内容,但其余的内容尚不清楚。
如果知道的话,希望有人讨论或支持。

  1. 使用 RepositoryExtendJPA.save(entity)
    示例:
    List<Person> person = this.PersonRepository.findById(0) person.setName("Neo"); This.PersonReository.save(person);
    此块代码更新了ID = 0;的记录的新名称;
  2. 使用javax或spring框架中的 @Transactional
    让@Transactional放在您的类或指定的函数上,都可以。
    我在某处读到,该注释在函数流结束时执行“提交”操作。因此,您在实体上修改的所有内容都会更新到数据库。