JPA 何时设置@GeneratedValue @Id

When does the JPA set a @GeneratedValue @Id

提问人:Raedwald 提问时间:2/1/2012 最后编辑:Manuel DrieschmannsRaedwald 更新时间:11/7/2017 访问量:50589

问:

我有一个简单的 JPA 实体,它使用生成的“ID”作为其主键:long

@Entity
public class Player {
   private long id;

   protected Player() {
     // Do nothing; id defaults to 0L
   }


   @GeneratedValue
   @Id
   public long getId() {
      return id;
   }

   protected void setId(final long id) {
      this.id = id;
   }
   // Other code
}

在此类型的对象生命周期中的某个时间点,JPA 必须调用以记录生成的 ID 值。我的问题是,这是什么时候发生的,说明这一点的文档在哪里。我浏览了 JPA 规范,找不到明确的声明。setId()

JPA 规范说(强调后加):

托管实体实例是具有持久性标识的实例,该标识当前与持久性上下文相关联。

这是想说必须管理对象才能具有其意义吗? 文档说(强调后加)它使“一个实例托管和持久化”,那么这是否意味着 是由该方法设置的?还是直到你打电话?@IdEntityManager.persist()@IdEntityTransaction.commit()

对于不同的 JPA 提供程序,以及对于不同的生成策略,设置的时间可能会有所不同。但是,对于已设置的生命周期中的最早点,您可以做出的最安全(可移植、符合规范)的假设是什么?@Id

Java JPA 主键

评论

0赞 skaffman 2/1/2012
听起来像是您可以通过调试轻松建立的东西。
3赞 Piotr Nowicki 2/1/2012
我敢打赌,如果规范没有明确说明何时应该生成@Id,那么就留给供应商来决定了。
0赞 skaffman 2/1/2012
@Raedwald:调试会告诉你 JPA 在内部是如何工作的,并告诉你哪些位是特定于方言的。
0赞 Raedwald 2/1/2012
相关但不重复的问题:stackoverflow.com/questions/8169640/......

答:

25赞 Thomas 2/1/2012 #1

调用 .persist() 不会自动设置 id 值。您的 JPA 提供程序将确保在实体最终写入 db 之前设置它。因此,您正确地假设在提交事务时将分配 id。但这不是唯一可能的情况。当您调用 .flush() 时,也会发生同样的情况。

托马斯

更新:请注意 Geek 的评论。-> 如果使用 GenerationType.Identity,则在将实体写入 db 之前,提供程序不会设置 id。在这种情况下,id 生成发生在 db 级别的插入过程中。无论如何,JPA 提供程序将确保实体在之后更新,并且生成的 ID 将在@Id注释属性中可用。

评论

1赞 Raedwald 2/1/2012
听起来很合理。那么,如果你打电话,你能依靠已经设置好吗?我在文档中找不到任何线索。EntityManager.flush()@Generated@Id
1赞 Thomas 2/1/2012
您可能还可以在休眠论坛上查看这篇文章: forum.hibernate.org/viewtopic.php?p=2384011#p2384011 这似乎取决于所选择的生成器策略
2赞 Thomas 2/1/2012
根据 EntityManager 文档 docs.oracle.com/javaee/6/api/javax/persistence/... flush 将“将持久性上下文同步到基础数据库”。这意味着所有池化的插入语句都将写入 db 以同步状态。为此,您的 JPA 提供程序需要这些 id 值。因此,它们在调用 flush 后应该可用。不过,有些策略可能会更早地设置它们。
0赞 Raedwald 2/1/2012
我想你已经找到了,但我希望文档更清晰。规范的对应部分是“3.2.4 同步到数据库:持久实体的状态在事务提交时同步到数据库。此同步涉及将任何更新写入数据库的持久性实体...[包括] 将新值分配给持久属性或字段...应用程序可以使用该方法来强制同步。flush
1赞 Geek 6/23/2014
@Thomas 你写道:“你的 JPA 提供程序将确保在实体最终写入 db 之前设置它”。这并不完全正确,因为如果您使用 ,那么它实际上仅在实体写入数据库之后可用,而不是在将其写入数据库之前。strategy=GenerationType.IDENTITY
12赞 JB Nizet 2/1/2012 #2

AFAIK,则仅保证在刷新持久性上下文时分配 ID。它可能会更快地分配,但这取决于生成策略。

11赞 Raedwald 2/26/2012 #3

Rubinger 和 Burke 合著的《Enterprise JavaBeans 3.1》一书在第 143 页(强调后加)中说:

Java Persistence 也可以配置为在通过使用主键字段或 setter 上的注释来调用 persist() 方法时自动生成主键。因此,在前面的示例中,如果我们启用了自动密钥生成,则可以在方法完成后查看生成的密钥。@GeneratedValuepersist()

JPA 规范说(强调后加):

托管实体实例是具有持久性标识的实例,该标识当前与持久性上下文相关联。

这也使得EntityManager.persist()

托管和持久实例

由于 对实体的身份至关重要,因此使对象受管理的唯一方法是通过生成 .@IdEntityManager.persist()@Id


然而

Rubinger 和 Buke 的明确声明与 Hibernate 的行为不一致。因此,知识渊博的人似乎对 JPA 规范的意图感到厌恶。

4赞 Jin Kwon 7/27/2016 #4

根据 JSR 338: JavaTM Persistence 2.1 / 3.5.3 Semantics of the Life Cycle Callback Methods for Entities

在实体持久化或删除实体后,将调用 and 回调方法。这些回调也将在级联这些操作的所有实体上调用。和 方法将分别在数据库插入和删除操作之后调用。这些数据库操作可能在调用持久性、合并或删除操作后直接发生,也可能在刷新操作发生后(可能在事务结束时)直接发生。生成的主键值在 PostPersist 方法中可用PostPersistPostRemovePostPersistPostRemove

一个可能的(个人推测的)异常是 GeneratorType.TABLE,容器(可能)提取要使用的值,并且(可能)将其设置为 。我总是使用我的 in .我不确定是否指定了此行为,或者可能不适用于任何其他供应商。PrePersistidPrePersist

重要编辑

并非所有应用程序服务器都设置了 id 。您可以跟踪JPA_SPECPrePersist