Hibernate没有正确级联双向关系

时间:2012-03-09 16:28:25

标签: java hibernate

我正在尝试在Hibernate中创建一对一的双向关系。在创建和保存新的子级和父级时,它会尝试使用对父级的空引用来持久保存子级。查看hibernate日志,它会为两个表生成id,然后将子对父对象的引用设置为null并执行查询。我完全糊涂了。

在父母的注释课程中,我有:

@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);

Hibernate日志,带有粗线的有趣行:

    [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")

在前两行粗体中,父亲知道孩子的身份,反之亦然。但是,在生成子级的sql时,它将父级的id设置为null。发生了什么事?

编辑: 作为参考,我使用以下hibernate jar: org.hibernate作为:冬眠的注解:罐子:3.3.1.GA org.hibernate作为:冬眠:罐子:3.2.6.ga

2 个答案:

答案 0 :(得分:6)

我不确切知道你为什么会这样做,但我所知道的是你没有这里的双向一对一关联。

您所拥有的是两个不同的一对一单向关联:父级使用子表的外键知道其子级,并且子级使用父表的外键知道其父级。

顺便说一句,这种做法可能会导致父母A有一个孩子B,而父母C有这样的情况。

如果您真正想要的是双向一对一关联,则应删除其中一个外键,并使用另一个来映射关联。假设您将CHILD_ID列保留在父表中,则映射将为:

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

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

答案 1 :(得分:0)

我修好了,虽然我不确定它为什么会起作用。我将级联从Parent移到了Child并保存了Child而不是Parent。

在父母的注释课程中,我有:

@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);