JPA 何时设置 @GeneratedValue @Id
Posted
技术标签:
【中文标题】JPA 何时设置 @GeneratedValue @Id【英文标题】:When does the JPA set a @GeneratedValue @Id 【发布时间】:2012-02-23 15:53:53 【问题描述】:我有一个简单的 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 提供程序而异,也可能因生成策略不同而异。但是,对于已设置的生命周期中的最早点,您可以做出的最安全(便携、符合规范)的假设是什么?
【问题讨论】:
听起来你可以通过调试轻松建立。 我敢打赌,如果规范没有明确说明 何时,则应该生成 @Id,而不是由供应商决定。 @Raedwald:调试会告诉你 JPA 在内部是如何工作的,并告诉你哪些位是特定于方言的。 相关但不重复的问题:***.com/questions/8169640/… 【参考方案1】:根据JSR 338: JavaTM Persistence 2.1/3.5.3 实体生命周期回调方法的语义,
在实体被持久化或删除后,将为实体调用
PostPersist
和PostRemove
回调方法。这些回调也将在这些操作级联到的所有实体上调用。PostPersist
和PostRemove
方法将分别在数据库插入和删除操作之后被调用。这些数据库操作可能在调用持久化、合并或删除操作后直接发生,也可能在刷新操作发生后直接发生(可能在事务结束时)。PostPersist
方法中提供了生成的主键值。
一个可能的(个人推测的)例外是GeneratorType.TABLE
,容器(可能)获取要使用的值并(可能)在PrePersist
之前设置它。我总是在PrePersist
中使用我的id
。我不确定此行为是否已指定或可能不适用于任何其他供应商。
重要修改
并非所有应用程序服务器都在PrePersist
之前设置id。你可以追踪JPA_SPEC。
【讨论】:
【参考方案2】:Enterprise JavaBeans 3.1 by Rubinger and Burke 这本书在第 143 页(添加了重点)说如下:
Java Persistence 还可以配置为在调用
persist()
方法时自动生成主键,方法是使用主键字段或设置器顶部的@GeneratedValue
注释。因此,在前面的示例中,如果我们启用了自动密钥生成,我们可以在persist()
方法完成后查看生成的密钥。
JPA 规范说(强调):
托管实体实例是具有持久性身份的实例,当前与持久性上下文相关联。
还有EntityManager.persist()
制作
托管和持久化的实例
由于@Id
对实体的身份 至关重要,EntityManager.persist()
管理对象的唯一方法是通过生成@Id
来建立其身份。 p>
但是
Rubinger 和 Buke 的明确声明与 Hibernate 的行为不一致。因此,知识渊博的人似乎不同意 JPA 规范的意图。
【讨论】:
【参考方案3】:调用 .persist() 不会自动设置 id 值。您的 JPA 提供程序将确保在实体最终写入 db 之前设置它。所以你假设在事务提交时将分配 id 是正确的。但这不是唯一可能的情况。当你调用 .flush() 时,同样会发生。
托马斯
更新:请注意 Geek 的评论。 -> 如果使用GenerationType.Identity,在实体写入db之前提供者不会设置id。在这种情况下,id 生成发生在 db 级别的插入过程中。无论如何,JPA 提供者将确保实体在之后更新,并且生成的 id 将在 @Id 注释属性中可用。
【讨论】:
听起来很合理。所以,如果你打电话给EntityManager.flush()
,你能相信@Generated
@Id
已经设置了吗?我在文档中找不到任何线索。
你也可以在hibernate论坛看看这个帖子:forum.hibernate.org/viewtopic.php?p=2384011#p2384011这似乎取决于选择的生成器策略
根据 EntityManager 文档docs.oracle.com/javaee/6/api/javax/persistence/…flush 将“将持久性上下文同步到底层数据库”。这意味着所有池化的插入语句都将写入 db 以同步状态。为此,您的 JPA 提供者需要这些 id 值。所以他们应该在调用flush后可用。不过,有些策略可能会更早设置它们。
我想你已经找到了,但我希望文档更清晰。规范的相应部分是“3.2.4 同步到数据库:持久实体的状态在事务提交时同步到数据库。这种同步涉及将持久实体的任何更新写入数据库...... [包括]分配持久属性或字段的新值...应用程序可以使用flush
方法来强制同步。"
@Thomas 您写道“您的 JPA 提供程序将确保在实体最终写入 db 之前设置它”。这并不完全正确,因为如果您使用的是strategy=GenerationType.IDENTITY
,那么它实际上只有在实体写入数据库之后才可用,而不是在它写入数据库之前。【参考方案4】:
AFAIK,只有在刷新持久性上下文时才能保证分配 ID。它可能会更早分配,但这取决于生成策略。
【讨论】:
以上是关于JPA 何时设置 @GeneratedValue @Id的主要内容,如果未能解决你的问题,请参考以下文章