JPA何时设置@GeneratedValue @Id

时间:2012-01-31 22:18:08

标签: java jpa primary-key

我有一个简单的JPA实体,它使用生成的long“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
}

在此类对象的生命周期中的某个时刻,JPA必须调用setId()来记录生成的ID值。我的问题是,发生这种情况时,哪里有说明这个的文档。我查看了JPA规范,找不到明确的声明。

JPA规范说(重点补充):

  

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

是否试图说该对象必须托管才能使其@Id显着? EntityManager.persist()的文档说(强调添加)它使“实例托管和持久”,这是否意味着@Id是由该方法设置的?或者直到你拨打EntityTransaction.commit()

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

4 个答案:

答案 0 :(得分:20)

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

托马斯

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

答案 1 :(得分:11)

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

答案 2 :(得分:8)

“企业版JavaBeans 3.1 by Rubinger and Burke”一书第143页(强调添加)中说明了以下内容:

  

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

JPA规范说(重点补充):

  

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

而且EntityManager.persist()使

  

实例托管和持久

由于@Id对于实体的身份至关重要,EntityManager.persist()管理对象的唯一方法是通过生成{{来建立其身份1}}。


<强>然而

Rubinger和Buke的清除语句与Hibernate的行为不一致。因此,知识渊博的人似乎对JPA规范的意图持怀疑态度。

答案 3 :(得分:4)

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

  

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

一个可能的(个人可能的)异常是GeneratorType.TABLE,容器(可能)获取要使用的值,并且(可能)在PrePersist之前设置它。我总是在id中使用PrePersist。我不确定这种行为是否已被指定,或者可能不适用于任何其他供应商。

重要编辑

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