JPA什么时候设置@GeneratedValue @Id

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

 @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 } 

在这种types的对象的生命周期的某个时刻,JPA必须调用setId()来logging生成的ID值。 我的问题是,这是什么时候发生的,以及说明这一点的文档在哪里 。 我查看了JPA规范,找不到明确的声明。

JPA规范说(重点补充):

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

那是不是试图说这个对象必须被pipe理来使其@Id有意义? EntityManager.persist()的文档说(强调添加)它使“一个实例pipe理和持久化”,所以这意味着@Id是由该方法设置? 或者直到你打电话给EntityTransaction.commit()

设置@Id对于不同的JPA提供者可能会有所不同,也可能是针对不同的生成策略。 但是,对于已经设定的生命周期中的最早点,您可以做出哪些最安全的(便携式,符合规范的)假设?

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

托马斯

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

AFAIK,只有当持久化上下文被刷新时才保证ID被分配。 它可能会尽快分配,但这取决于生成策略。

由Rubinger和Burke撰写的“企业JavaBeans 3.1 ”一书在第143页上说了以下内容(着重部分):

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

JPA规范说(重点补充):

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

还有EntityManager.persist()使

一个pipe理和持久的实例

由于@Id对实体的身份至关重要,因此EntityManager.persist()使对象受pipe理的唯一方法是通过生成@Id来build立其身份。


然而

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

根据JSR 338:JavaTM Persistence 2.1 / 3.5.3实体的生命周期callback方法的语义

PostPersistPostRemovecallback方法是在实体持久化或移除之后为实体调用的。 这些callback也将在这些操作级联的所有实体上被调用。 PostPersistPostRemove方法将分别在数据库插入和删除操作之后调用。 这些数据库操作可能在调用persist,merge或remove操作之后直接进行,也可能在flush操作发生后(可能在事务结束时)直接发生。 生成的主键值在PostPersist方法中可用

一个可能的(个人可能的)exception是容器(可能)提取值使用的GeneratorType.TABLE和(可能)在PrePersist之前设置它。 我总是在PrePersist使用我的id 。 我不确定这种行为是否被指定,或者可能无法与任何其他供应商合作。

重要的编辑

并非所有应用程序服务器都在PrePersist之前设置了ID。 您可以跟踪JPA_SPEC 。