如何在Spring Data中漂亮地更新JPA实体?

imzjd6km  于 2023-08-06  发布在  Spring
关注(0)|答案(8)|浏览(215)

所以我看了各种关于JPA与Spring Data的教程,在很多场合都是不同的,我不太确定正确的方法是什么。
假设存在以下实体:

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中更改他的名称-然后将有一些控制器动作,其中将有具有旧ID和新名称的更新的DTO。
现在我必须将此更新的DTO保存到数据库。
不幸的是,目前没有办法更新现有的客户(除了删除DB中的条目并使用新的自动生成的ID创建一个新的客户)
然而,由于这是不可行的(特别是考虑到这样的实体可能有数百个潜在的关系)-所以我想到了两个直接的解决方案:
1.在Customer类中为id创建一个setter-从而允许设置id,然后通过相应的存储库保存Customer对象。
或者是
1.将id字段添加到构造函数中,每当你想更新一个客户时,你总是用旧的id创建一个新的对象,但其他字段的新值(在本例中只有名称)
所以我的问题是,是否有一个一般规则如何做到这一点?我所解释的两种方法的缺点是什么?

l7mqbcuq

l7mqbcuq1#

T getById(ID id)自2.7起已弃用。Please use T getReferenceById(ID id) instead.的思想是一样的,它只返回一个引用。但它更容易自我解释,因为“getById”可能不清楚,而“getReferenceById”则不那么模糊。

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

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

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


Is更好,因为getOne(ID id)只获取一个引用(代理)对象,而不从DB中获取它。在这个引用上,你可以设置你想要的,在save()上,它只会像你期望的那样执行一个SQL UPDATE语句。相比之下,当你调用find()时,比如在@Tanjim Rahmans answer spring data中,JPA将执行SQL SELECT来从DB中物理地获取实体,当你只是更新时,你不需要它。

r8xiu3jd

r8xiu3jd2#

在SpringData中,如果您有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);

  }

字符串
一些解决方案声称使用Spring Data 并执行JPA老派(即使以丢失更新的方式)。

wz3gfoph

wz3gfoph3#

简单的JPA更新..

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

字符串

shstlldc

shstlldc4#

在JpaRepository中有一个方法

getOne

字符串
目前已弃用,而改用

getById


所以正确的方法应该是

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

iqxoj9l9

iqxoj9l95#

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

aelbi1ox

aelbi1ox6#

如果您需要直接使用DTO而不是实体,则应检索现有Customer示例,并将DTO中的更新字段Map到该示例。

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

字符串

hfwmuf9z

hfwmuf9z7#

因此,现在假设客户想要在webui中更改他的名称-然后将有一些控制器动作,其中将有具有旧ID和新名称的更新的DTO。
通常,您有以下工作流:
1.用户向服务器请求数据并在UI中获取数据;
1.用户更正其数据,并将其发送回服务器与已经存在的ID;
1.在服务器上,你通过用户获取更新数据的DTO,通过ID在DB中找到它(否则抛出异常),然后用所有给定的数据,外键等转换DTO -> Entity。
1.然后你只需要merge它,或者如果使用Spring Data调用保存(),它又会merge它(参见thread);

**P.S.**此操作将不可避免地发出2个查询:选择并更新。同样,2个查询,即使你想更新一个字段。然而,如果你在实体类的顶部使用Hibernate专有的@DynamicUpdate注解,它将帮助你不包括所有的字段,而只包括那些实际改变的字段。
**P.S.**如果你不想为第一个select语句付费,而更喜欢使用Spring Data的@Modifying查询,那么做好丢失与可修改实体相关的L2 C缓存区域的准备;甚至更糟糕的情况与本机更新查询(见此thread),当然也准备手动编写这些查询,测试他们,并支持他们在未来。

jtjikinw

jtjikinw8#

我遇到了这个问题!
幸运的是,我确定了 2种方法 ,并了解了一些事情,但其余的不清楚。
希望有认识的人讨论或支持。
1.使用RepositoryExtendJPA.保存(entity)
范例:
第一个月
此块代码更新了id = 0的记录的新名称;
1.使用javax或spring框架中的
@Transactional**。
让我们把@Transactional放在你的类或指定的函数上,两者都可以。
我在某个地方读到过,这个注解在函数流的末尾做了一个“提交”动作。因此,您在实体中修改的所有内容都将更新到数据库中。

相关问题