[英]When does the JPA set a @GeneratedValue @Id
我有一个简单的 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()
?
对于不同的 JPA 提供者,以及可能对于不同的生成策略,设置@Id
时间可能会有所不同。 但是,您可以对生命周期中的最早点做出的最安全(便携、符合规范)假设是什么?
调用 .persist() 不会自动设置 id 值。 您的 JPA 提供程序将确保在实体最终写入 db 之前设置它。 因此,您可以假设在提交事务时分配 id 是正确的。 但这并不是唯一可能的情况。 当你调用 .flush() 时,同样会发生。
托马斯
更新:请注意 Geek 的评论。 -> 如果使用 GenerationType.Identity,则在实体写入 db 之前提供者不会设置 id。 在这种情况下,id 生成发生在 db 级别的插入过程中。 无论如何,JPA 提供者将确保实体随后更新,并且生成的 id 将在 @Id 注释属性中可用。
AFAIK,只有在刷新持久性上下文时才保证分配 ID。 它可能会更快分配,但这取决于生成策略。
Rubinger 和 Burke所著的 Enterprise JavaBeans 3.1一书在第 143 页(添加了强调)如下:
Java Persistence 还可以配置为在通过使用主键字段或 setter 顶部的
@GeneratedValue
注释调用persist()
方法时自动生成主键。 因此,在前面的示例中,如果我们启用了自动密钥生成,则可以在persist()
方法完成后查看生成的密钥。
JPA 规范说(强调):
受管实体实例是具有当前与持久上下文相关联的持久标识的实例。
还有EntityManager.persist()
使得
一个实例管理和持久化
由于@Id
对实体的身份至关重要,因此EntityManager.persist()
管理对象的唯一方法是通过生成@Id
来建立其身份。
然而
Rubinger 和 Buke 的明确声明与 Hibernate 的行为不一致。 因此,似乎知识渊博的人不同意 JPA 规范的意图。
根据JSR 338: JavaTM Persistence 2.1 / 3.5.3 Semantics of the Life Cycle Callback Methods for Entities ,
PostPersist
和PostRemove
回调方法在实体被持久化或删除后被调用。 这些回调也将在这些操作级联到的所有实体上调用。PostPersist
和PostRemove
方法将分别在数据库插入和删除操作之后被调用。 这些数据库操作可能在调用持久、合并或删除操作之后直接发生,也可能在刷新操作发生后(可能在事务结束时)直接发生。 生成的主键值在PostPersist
方法中可用。
一种可能的(个人推测的)异常是GeneratorType.TABLE
,容器(可能)获取要使用的值并(可能)在PrePersist
之前设置它。 我总是在PrePersist
使用我的id
。 我不确定此行为是否已指定或可能不适用于任何其他供应商。
重要编辑
并非所有应用程序服务器都在PrePersist
之前设置 id。 您可以跟踪JPA_SPEC 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.