【问题标题】:Jpa Repository save() doesn't update existing dataJpa Repository save() 不更新现有数据
【发布时间】:2020-03-22 18:11:37
【问题描述】:

我正在尝试更新数据,据我所知,如果 id 为 null,save() 方法会保存实体,如果在 DB 中找到给定的 id,则更新数据库中的现有实体。

但是,当我尝试保存数据时,它没有更新:

public Employer update() {
    Employer emp = Employer.builder()
        .id(2L) // it exists in database
        .name('new company name')
        .build();

    return repository.save(emp);
}

但是,当我从数据库中检索数据并更新其字段并再次保存时,它会更新:

public Employer update() {
    Employer emp = repository.getOne(2L);
    emp.setName('new company name');

    return repository.save(emp);
}

谁能解释这种行为的原因?我阅读了文档,但找不到与此相关的任何内容。

这是我的存储库:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface Employer extends JpaRepository<Employer, Long> {

}

和实体:

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(schema = "public", name = "employer")
public class Employer {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @NotBlank
  @Size(max = 50)
  private String name;

}

【问题讨论】:

  • 可以添加Employer的代码来查看JPA注解吗?
  • @WimDeblauwe 我添加了 Employer 实体
  • 你如何测试这个?通过单元/集成测试还是在应用程序中?你在使用交易吗?
  • 我在一个应用程序中测试,每2分钟调用一次服务方法,我每次都检查数据库。在第一种情况下,我构建实体并保存它不起作用,但在后一种情况下,当我从数据库中检索数据并更新其字段并再次保存时,它会更新。
  • update() 方法上尝试@Transactional 注释。希望这可能会奏效..

标签: java spring-boot jpa spring-data-jpa


【解决方案1】:

这个问题已经得到解答,但这是我对主题的理解,因为我最近开始研究它。

这可以根据瞬态和持久/托管实体来回答。

瞬态实体 : 一个新的实体对象被创建,直到现在还没有与任何会话相关联。该对象与数据库中存储的任何数据无关,因为它是新创建的。

当您从 db 获取记录时,它会以托管或持久状态获取,对其所做的任何更改都将反映回映射到的记录。

建议 1:您不应该手动添加主键值,因为它已经是 AutoIncremented。

建议2:如果你已经有recirds id,先从db中获取,然后更新。

这里是关于该主题的 Stackoverflow 讨论,可以让您更深入地了解:What are detached, persistent and transient objects in hibernate?

【讨论】:

    【解决方案2】:

    要通过 JPA 更新现有的实体对象,它应该首先提供给实体管理器。

    通读文章

    https://www.objectdb.com/java/jpa/query/jpql/update

    摘自同一篇

    1.Retrieving the entity objects into an EntityManager.
    2.Updating the relevant entity object fields within an active transaction.
    3.Applying changes to the database by calling the commit method.
    

    我假设对于没有发生更新的情况,该实体还不是托管实体。

    关于托管实体的更多信息:https://www.baeldung.com/hibernate-entity-lifecycle#managed-entity

    【讨论】:

      【解决方案3】:

      您的实体Employer 看起来处于分离/瞬态状态,您正在手动传递id 值,这是不允许的,因为它被标记为@GeneratedValue(strategy = GenerationType.IDENTITY)

      您需要做的是当您知道主键值即id 值时,首先使用 findById() 方法从数据库中获取实体,通过该方法实体进入托管状态,然后尝试更新实体通过调用 save() 方法。这将更新您的实体。

      有关实体状态的更多信息,您可以参考: https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/

      【讨论】:

      • 真的很伤心。在某些情况下,您知道主键是什么。您必须进行 2 个 db 调用而不是 1 个,我不明白为什么这是必要的。
      猜你喜欢
      • 2019-05-28
      • 2017-08-04
      • 1970-01-01
      • 2016-02-05
      • 1970-01-01
      • 2020-05-23
      • 2020-12-11
      • 2021-06-28
      • 2021-08-08
      相关资源
      最近更新 更多