2012-01-31 112 views
36

我有一個使用一個生成long「ID」作爲其主鍵的簡單JPA實體:在對象的生命週期什麼時候JPA設置@GeneratedValue @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

聽起來像你可以很容易地建立調試的東西。 – skaffman 2012-01-31 22:41:01

+3

我敢打賭,如果規範沒有明確地說*應該生成@Id,那麼它就要由供應商決定。 – 2012-01-31 22:43:48

+0

@Rededwald:調試將告訴你JPA如何在內部工作,並告訴你哪些位是特定於方言的。 – skaffman 2012-01-31 22:48:29

回答

19

調用.persist()將不會自動設置id的值。您的JPA提供程序將確保在實體最終寫入db之前它已被設置。所以你是對的,假設在事務提交時id會被分配。但這不是唯一可能的情況。當你打電話給.flush()時,會發生同樣的情況。

Thomas

更新:請關注Geek的評論。 - >如果使用GenerationType.Identity,則在將實體寫入數據庫之前,該提供程序不會設置該ID。在這種情況下,id生成在db級別的插入過程中發生。無論如何,JPA提供者將確保實體隨後被更新,並且生成的ID將在@Id註釋的屬性中可用。

+1

聽起來很合理。所以,如果你調用'EntityManager.flush()'你能依賴已經設置了@生成的@ Id嗎?我在文檔中找不到任何線索。 – Raedwald 2012-01-31 22:50:08

+1

你也許還會在hibernate論壇看看這篇文章:https://forum.hibernate.org/viewtopic.php?p=2384011#p2384011 這似乎取決於選擇發生器策略 – Thomas 2012-01-31 22:57:11

+1

根據EntityManager文檔http ://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#flush()flush將「將持久性上下文同步到基礎數據庫」。這意味着所有池化插入語句都將被寫入數據庫以同步狀態。爲此,您的JPA提供程序需要這些id值。所以他們應該在調用flush之後可用。儘管如此,有些策略可能會提前設置它 – Thomas 2012-01-31 23:01:31

11

AFAIK,該ID只保證當持久上下文刷新到被分配。它可能會盡快分配,但它取決於生成策略。

7

書企業JavaBeans 3.1 by Rubinger and Burke說以下,143頁(強調)上:

Java持久性也可以被配置爲自動生成主當persist()方法是通過使用調用關鍵在主鍵字段或設置者之上註釋@GeneratedValue。因此,在前面的示例中,如果我們啓用了自動密鑰生成,則可以在完成persist()方法後查看生成的密鑰。

JPA規範表示(強調):

甲管實體實例是具有持久身份當前與一個持久性上下文相關聯的實例。

而且也是EntityManager.persist()使得

實例進行管理和持續

由於@Id是一個實體的身份,爲EntityManager.persist()做出的唯一途徑至關重要管理的對象是通過生成@Id來建立其身份。


然而

Rubinger和布克的明確的說法是與Hibernate的行爲不一致。因此,知識淵博的人似乎對JPA規範的意圖不滿。

4

根據對實體生命週期回調方法的JSR 338: JavaTM Persistence 2.1/3.5.3語義,

PostPersistPostRemove調用回調方法爲一個實體的實體已變得持久或刪除後。這些回調也將在這些操作級聯的所有實體上調用。分別在數據庫插入和刪除操作後將調用PostPersistPostRemove方法。這些數據庫操作可能會在調用persist,merge或remove操作之後直接進行,也可能在發生刷新操作後(可能在事務結束時)直接發生。 生成的主鍵值可在PostPersist方法中獲得。

的一種可能的(個人推測)例外是GeneratorType.TABLE其中容器(5月)中取值使用和PrePersist之前(可以)設置。我總是在PrePersist中使用我的id。我不確定這種行爲是否被指定,或者可能不適用於任何其他供應商。

重要編輯

並非所有的應用服務器PrePersist之前設置的ID。您可以跟蹤JPA_SPEC

+0

好點。不幸的是有人投票。 – Gilberto 2017-03-31 18:08:09

相關問題