我有一个简单的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提供程序可能会有所不同,也可能针对不同的生成策略。但是,最安全的(可移植的,符合规范的)假设是什么,你可以对生命周期中最早的点进行设置?
答案 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实体生命周期回调方法的语义,
在实体被持久化或删除后,将为实体调用
PostPersist
和PostRemove
回调方法。这些回调也将在这些操作级联的所有实体上调用。分别在数据库插入和删除操作之后将调用PostPersist
和PostRemove
方法。这些数据库操作可能在调用了持久,合并或删除操作后直接发生,或者可能在刷新操作发生后直接发生(可能在事务结束时)。PostPersist
方法中提供了生成的主键值。
一个可能的(个人可能的)异常是GeneratorType.TABLE
,容器(可能)获取要使用的值,并且(可能)在PrePersist
之前设置它。我总是在id
中使用PrePersist
。我不确定这种行为是否已被指定,或者可能不适用于任何其他供应商。
重要编辑
并非所有应用程序服务器都在PrePersist
之前设置了ID。您可以跟踪JPA_SPEC。