2013-11-29 34 views
3

我正在使用Hibernate 5和Spring 3.2.4。我設計了一個用戶實體,我想在其中包含對創建該實體的用戶的引用 - 所以這是一個自引用。自引用本身不是太有問題,但我想將該字段指定爲非null。這可能嗎?如果字段非空,那麼如何在數據庫中創建第一個條目,因爲引用的實體尚不存在?Hibernate自引用實體爲非空列

例:

@Entity 
public class User { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "id") 
    private Long User.id; 

    @NotNull 
    private String username; 

    private String password; 

    @NotNull 
    private User user; 


    // getters and setters omitted for brevity 
} 

如果我嘗試:

User u = new User(); 
u.setUsername("username"); 
u.setCreatedBy(u); 

,並嘗試堅持u,我收到以下錯誤信息:

Caused by: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.domain.User.createdBy -> com.domain.User 

我明白Hibernate的抱怨它不能引用一個瞬態實例(即:u),但我不能rsist u除非我有一個我可以參考的非空用戶。但是在一個空的DB中,沒有這樣的入口。

這種配置是不可能做到的嗎?或者有沒有辦法解決這個問題?

+0

您是否試過用@ManyToOne指定映射?此外,我沒有看到標識符字段,您是否像引用者一樣省略了它? –

+0

@ elusive-code - 我更新了沒有'@ Roo'註解的實體以顯示其他文件。是的 - 爲了簡潔起見,我已經省去了獲取者/設置者。根據我的理解,@ManyToOne和@NotNull之間的區別在於@ManyToOne將用於DDL生成中,並且在持久化bean時,即使不持久化Bean,@NotNull也可用於bean驗證。 –

回答

3

我不明白這Roo註釋,我不使用Hibernate特有的註釋,只有JPA。我對自引用沒有任何問題。但我對你有一些提示:

  1. 如前所述,使用@ManyToOne註釋。
  2. AFAIK,@NotNull註釋(或nullable字段@Column)不影響映射,只有DDL生成。我不使用域模型中的DDL生成,我從來沒有指定過。相反,我使用@ManyToOneoptional字段。
  3. 您使用什麼標識符生成策略?如果自動增量,則不可能用NOT NULL約束自引用。所以要麼使用基於序列的標識符生成器或移除約束。我會先用。
  4. 正如我所提到的,當你有NOT NULL約束時,設置optional字段@ManyToOnefalse。否則,Hibernate嘗試進行兩個查詢:插入createdBy_id設置爲NULL,然後更新createdBy_id。第一個查詢失敗,並啓用NOT NULL限制。
+0

我編輯了實體以提供沒有'@ Roo'註釋的示例。是的 - 我爲這張表使用自動增量,沒有考慮使用其他GUID策略。我會嘗試一個基於序列的標識符。然而,根據我的理解,Hibernate註釋與'@NotNull'之間的區別在於前者將確保數據庫模式設置,而後者將在bean驗證上工作,即使不保留到數據庫。 –

+0

我通過自動生成更改爲表生成(MySQL不支持序列)。現在似乎按需要工作。謝謝! –

0

u的字段createdBy由於註釋@NOTNULL而不能爲空。但u反省自己,並保存它之前不堅持。 您可以爲u設置另一個持續User,而不是自己。

+0

您的假設與自動增量主鍵生成是正確的。但是當使用基於序列的PK發生器時,ID在插入之前已經是已知的。所以它可能會執行一個有效的INSERT查詢。 –

+1

就像我在我原來的文章中所說的,我知道這是導致問題的原因。然而問題仍然存在 - 你如何創建表中的第一行?沒有其他用戶可以分配給它。 –

0

我找到了解決方案。您必須爲您的ID使用序列生成器(而不是默認的自動生成的ID)。然後它工作。

@Entity 
public class UserModel { 
    /** 
    * We must use a sequence for generating IDs, 
    * because of the self reference . 
    * https://vladmihalcea.com/2014/07/15/from-jpa-to-hibernates-legacy-and-enhanced-identifier-generators/ 
    */ 
    @Id 
    @GeneratedValue(generator = "sequence", strategy=GenerationType.SEQUENCE) 
    @SequenceGenerator(name = "sequence", allocationSize = 10) 
    private Long id; 

    /** reference to a parent user, e.g. the manager */ 
    @ManyToOne(optional = false) 
    @NotNull 
    UserModel parentUser; 

    [...] 

原因如下:當Hibernate嘗試插入新用戶時,它也會嘗試驗證對parentUser的引用。但是,對於我們想要插入的第一個用戶來說,這會失敗,或者當用戶引用他自己時也會失敗。

但是,當使用序列生成ID時,在插入時已經知道新/下一個ID。