2012-03-09 72 views
0

我在嘗試在Hibernate中創建的一對一雙向關係遇到問題。在創建並保存新的子項和父項時,它會嘗試使用對父項的空引用持久化子項。查看休眠日誌,它會爲這兩個表生成ID,然後將子對父對象的引用設置爲空並執行查詢。我完全困惑。休眠不正確級聯雙向關係

在父母的註解類,我有:

@OneToOne 
@JoinColumn(name = "CHILD_ID") 
@Cascade(CascadeType.ALL) 
private Child child; 

在孩子的註解類,我有:

@OneToOne 
@JoinColumn(name = "PARENT_ID") 
private Parent parent; 

對於創建/堅持,我有:

Parent p = new Parent(); 
Child c = new Child(); 
p.setChild(c); 
c.setParent(p); 
getHibernateTemplate().save(p); 

休眠日誌,有趣的粗線:

 
    [org.springframework.orm.hibernate3.SessionFactoryUtils] [Opening Hibernate Session] 
    [org.hibernate.impl.SessionImpl] [opened session at timestamp: 13313040615] 
    [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [saving transient instance] 
    [org.hibernate.jdbc.AbstractBatcher] [opening JDBC connection] 
    [org.hibernate.SQL] [select UNQE_KEY_VALU from where UNQE_KEY_NAME = '' for update] 
    [org.hibernate.jdbc.AbstractBatcher] [closing JDBC connection (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)] 
    [org.hibernate.id.MultipleHiLoPerTableGenerator] [new hi value: 1413946] 
    [org.hibernate.event.def.AbstractSaveEventListener] [generated identifier: 14139460, using strategy: org.hibernate.id.MultipleHiLoPerTableGenerator] 
    [org.hibernate.event.def.AbstractSaveEventListener] [saving [Parent#14139460]] 
    [org.hibernate.engine.Cascade] [processing cascade ACTION_SAVE_UPDATE for: Parent] 
    [org.hibernate.engine.CascadingAction] [cascading to saveOrUpdate: Child] 
    [org.hibernate.engine.IdentifierValue] [id unsaved-value: 0] 
    [org.hibernate.event.def.AbstractSaveEventListener] [transient instance of: Child] 
    [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [saving transient instance] 
    [org.hibernate.jdbc.AbstractBatcher] [opening JDBC connection] 
    [org.hibernate.SQL] [select UNQE_KEY_VALU from where UNQE_KEY_NAME = '' for update] 
    [org.hibernate.jdbc.AbstractBatcher] [closing JDBC connection (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)] 
    [org.hibernate.id.MultipleHiLoPerTableGenerator] [new hi value: 1413947] 
    [org.hibernate.event.def.AbstractSaveEventListener] [generated identifier: 14139470, using strategy: org.hibernate.id.MultipleHiLoPerTableGenerator] 
    [org.hibernate.event.def.AbstractSaveEventListener] [saving [Child#14139470]] 
    [org.hibernate.engine.Cascade] [done processing cascade ACTION_SAVE_UPDATE for: Child] 
    [org.hibernate.engine.Cascade] [processing cascade ACTION_SAVE_UPDATE for: Parent] 
    [org.hibernate.engine.Cascade] [done processing cascade ACTION_SAVE_UPDATE for: Parent] 
    [org.springframework.orm.hibernate3.HibernateTemplate] [Eagerly flushing Hibernate session] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [flushing session] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [processing flush-time cascades] 
    [org.hibernate.engine.Cascade] [processing cascade ACTION_SAVE_UPDATE for: Parent] 
    [org.hibernate.engine.CascadingAction] [cascading to saveOrUpdate: Child] 
    [org.hibernate.event.def.AbstractSaveEventListener] [persistent instance of: Child] 
    [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [ignoring persistent instance] 
    [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [object already associated with session: [Child#14139470]] 
    [org.hibernate.engine.Cascade] [done processing cascade ACTION_SAVE_UPDATE for: Parent] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [dirty checking collections] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [Flushing entities and processing referenced collections] 
    [org.hibernate.persister.entity.AbstractEntityPersister] [Child.parent is dirty] 
    [org.hibernate.event.def.DefaultFlushEntityEventListener] [Updating entity: [Child#14139470]] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [Processing unreferenced collections] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [Scheduling collection removes/(re)creates/updates] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [Flushed: 2 insertions, 1 updates, 0 deletions to 2 objects] 
    [org.hibernate.event.def.AbstractFlushingEventListener] [Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections] 
    [org.hibernate.pretty.Printer] [listing entities:] 
    [org.hibernate.pretty.Printer] [Parent{child=Child#14139470, parentId=14139460}] 
    [org.hibernate.pretty.Printer] [Child{parent=Parent#14139460, childId=14139470}] 
[org.hibernate.event.def.AbstractFlushingEventListener] [executing flush] 
    [org.hibernate.jdbc.ConnectionManager] [registering flush begin] 
    [org.hibernate.persister.entity.AbstractEntityPersister] [Inserting entity: [Child#14139470]] 
    [org.hibernate.jdbc.AbstractBatcher] [about to open PreparedStatement (open PreparedStatements: 0, globally: 0)] 
    [org.hibernate.jdbc.ConnectionManager] [opening JDBC connection] 
    [org.hibernate.SQL] [insert into CHILD_TABLE (PARENT_ID, CHILD_ID) values (?, ?)] 
    [org.hibernate.jdbc.AbstractBatcher] [preparing statement] 
    [org.hibernate.persister.entity.AbstractEntityPersister] [Dehydrating entity: [Child#14139470]] 
    [org.hibernate.type.LongType] [binding null to parameter: 1] 
    [org.hibernate.type.LongType] [binding '14139470' to parameter: 2] 
    [org.hibernate.persister.entity.AbstractEntityPersister] [Inserting entity: [Parent#14139460]] 
    [org.hibernate.jdbc.AbstractBatcher] [Executing batch size: 1] 
    [org.hibernate.jdbc.AbstractBatcher] [about to close PreparedStatement (open PreparedStatements: 1, globally: 1)] 
    [org.hibernate.jdbc.AbstractBatcher] [closing statement] 
    [DEBUG] [org.hibernate.util.JDBCExceptionReporter] [Could not execute JDBC batch update [insert into CHILD_TABLE (PARENT_ID, CHILD_ID) values (?, ?)]] 
java.sql.BatchUpdateException: ORA-01400: cannot insert NULL into ("CHILD_TABLE"."PARENT_ID") 

在前兩條粗體行中,父級知道孩子的ID,反之亦然。但是,當生成孩子的sql時,它將父母的ID設置爲空。到底是怎麼回事?

編輯: 作爲參考,我使用下面的休眠罐: org.hibernate作爲:冬眠的註解:罐子:3.3.1.GA org.hibernate作爲:冬眠中:jar:3.2.6.ga

回答

0

我固定它,雖然我不知道爲什麼它的工作原理。我將級聯從父級移到子級,並保存孩子而不是父級。

在父母的註解類,我有:

@OneToOne 
@JoinColumn(name = "CHILD_ID") 
private Child child; 

在孩子的註解類,我有:

@OneToOne 
@JoinColumn(name = "PARENT_ID") 
@Cascade(CascadeType.ALL) 
private Parent parent; 

對於創建/堅持,我有:

Parent p = new Parent(); 
Child c = new Child(); 
p.setChild(c); 
c.setParent(p); 
getHibernateTemplate().save(c); 
5

我不知道爲什麼你會得到這種行爲,但我知道的是,你在這裏沒有雙向的一對一關聯。

你所擁有的是兩種不同的一對一單向關聯:父對子表使用一個外鍵來知道它的子對象,並且子對父對象使用一個外鍵來知道它的父對象。

順便說一句,這樣做的這種方式可能導致其中父A具有有父C.

如果你真的想要的是一個雙向one-to-one關聯,你應該在小孩B的情況刪除其中一個外鍵,然後使用另一個來映射關聯。假設你保持父表中的列CHILD_ID,映射將被:

@OneToOne 
@JoinColumn(name = "CHILD_ID") 
@Cascade(CascadeType.ALL) 
private Child child; 

@OneToOne(mappedBy = "child") 
private Parent parent; 
+0

不幸的是,我堅持使用db結構。如果我所擁有的是兩種不同的一對一單向關聯,那麼這就是我必須要處理的。我按原樣嘗試了你的java修改(沒有修改表),並且收到了和以前一樣的錯誤。 – Josh 2012-03-09 17:22:19

+0

嘗試將Child實體中JoinColumn註解的nullable屬性設置爲false,以告知Hibernate禁止在此列中使用null值。 – 2012-03-09 17:32:16

+0

當我添加nullable = false到Child實體的JoinColumn註解時,hibernate生成的sql現在甚至不會嘗試持久parent_id。這仍然會導致錯誤,因爲該列在db中定義爲不可空。 – Josh 2012-03-09 17:51:51