【问题标题】:Spring data JPA does not allow an Entity to be peristed if the primary key is not null如果主键不为空,Spring data JPA 不允许实体被持久化
【发布时间】:2017-03-10 07:33:33
【问题描述】:

我有一个订阅者实体,它使用用户提供的电子邮件地址作为主键,而不是自动生成的值。这意味着当调用JpaRepository的save方法时,主键值是not null

Spring 数据 JPA documentation 第 2.2.1 节表 2.2 说明如下:

默认情况下,Spring Data JPA 检查给定的 Id-Property 实体。如果 Id-Property 为空,则实体将被假定为 新的,否则视为不是新的。

这种行为会阻止新的订阅者实体被持久化到数据库中。

【问题讨论】:

  • 不应该。当 repo 看到 ID 存在于输入实体中时,它将在内部调用 EntityManager.merge() ,它仍应保存您的实体。您只需要记住将repository.save()的返回值作为托管实体
  • @AdrianShum 你是对的。现在可以了。但是,未检测到重复项。如果我尝试两次保存同一个实体,第二次它不会给出重复的异常,因为我使用自动生成的密钥。而是更新了同一个实体,这不是我想要的。

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


【解决方案1】:

在决定如何选择实体的主键时,我们有两种选择。 1.使用Spring提供的自动生成密钥 2. 使用自定义主键,例如一个电子邮件地址。

自动生成的密钥更易于使用。在持久化实体时,Spring 注意到 id 字段为空,并得出结论认为这是一个新实体正在持久化。一个新的自动生成的值被分配给 id 字段并且实体被持久化。但是,如果您想确保不保留具有相同电子邮件地址的两个实体,请记住使用 @Column(unique="true") 注释电子邮件字段。由于电子邮件字段的唯一约束,检测重复项也很容易。

但是,有时您不想使用自动生成的密钥,因为您可能希望使用用户提供的电子邮件地址作为密钥。这种方法没有问题。使用@Id 标记实体中的电子邮件字段。就这样。但是,无法进行重复检测。如果多次收到创建具有相同电子邮件地址的实体的请求,则每次都会更新同一个实体,即每次都会更新EntityManager.merge()。不会引发约束违反异常。回想一下,Spring 总是检查主键字段是否为空,以决定是创建新实体还是合并到现有实体中。

【讨论】:

  • 多么奇怪的春季怪癖。在确定实体是否为新实体时,检查@Version 列可能更有意义。事实上,这意味着所有具有非自动生成 @Id 的实体都不是新的......
【解决方案2】:

The same table 告诉您如何自定义该行为。

要么让您的实体实现Persistable 并覆盖isNew,要么提供EntityInformation 的自定义实现。

【讨论】:

  • 我知道 isNew() 方法。我可以调用 findByEmail() 并检查实体是否存在。但这需要我想避免的额外数据库操作。
  • 您是说您不知道如何确定您的实体是否是新的,但期望 Spring Data 开箱即用?我担心这要求太多了。
  • 在我之前对您的评论之后,我想出了如何使用电子邮件 ID 作为密钥。您可以参考我上面对@AdrianShum 的评论。现在的问题是 save 也会更新实体而不是给出重复的异常
猜你喜欢
  • 2014-10-20
  • 2018-05-01
  • 1970-01-01
  • 2011-09-22
  • 2016-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多