【问题标题】:Hibernate not correctly cascading bi-directional relationshipHibernate 没有正确级联双向关系
【发布时间】:2012-03-27 02:38:07
【问题描述】:

我在尝试在 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] [打开休眠会话] [org.hibernate.impl.SessionImpl] [在时间戳打开会话:13313040615] [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [保存瞬态实例] [org.hibernate.jdbc.AbstractBatcher] [打开JDBC连接] [org.hibernate.SQL] [select UNQE_KEY_VALU from where UNQE_KEY_NAME = '' for update] [org.hibernate.jdbc.AbstractBatcher] [关闭 JDBC 连接(打开 PreparedStatements:0,全局:0)(打开 ResultSets:0,全局:0)] [org.hibernate.id.MultipleHiLoPerTableGenerator] [新喜值:1413946] [org.hibernate.event.def.AbstractSaveEventListener] [生成标识符:14139460,使用策略:org.hibernate.id.MultipleHiLoPerTableGenerator] [org.hibernate.event.def.AbstractSaveEventListener] [保存 [Parent#14139460]] [org.hibernate.engine.Cascade] [处理级联 ACTION_SAVE_UPDATE 为:父级] [org.hibernate.engine.CascadingAction] [级联到 saveOrUpdate: Child] [org.hibernate.engine.IdentifierValue] [id unsaved-value: 0] [org.hibernate.event.def.AbstractSaveEventListener] [瞬态实例:子] [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [保存瞬态实例] [org.hibernate.jdbc.AbstractBatcher] [打开JDBC连接] [org.hibernate.SQL] [select UNQE_KEY_VALU from where UNQE_KEY_NAME = '' for update] [org.hibernate.jdbc.AbstractBatcher] [关闭 JDBC 连接(打开 PreparedStatements:0,全局:0)(打开 ResultSets:0,全局:0)] [org.hibernate.id.MultipleHiLoPerTableGenerator] [新喜值:1413947] [org.hibernate.event.def.AbstractSaveEventListener] [生成标识符:14139470,使用策略:org.hibernate.id.MultipleHiLoPerTableGenerator] [org.hibernate.event.def.AbstractSaveEventListener] [保存 [Child#14139470]] [org.hibernate.engine.Cascade] [为:子级完成处理级联 ACTION_SAVE_UPDATE] [org.hibernate.engine.Cascade] [处理级联 ACTION_SAVE_UPDATE 为:父级] [org.hibernate.engine.Cascade] [完成处理级联 ACTION_SAVE_UPDATE 为:父] [org.springframework.orm.hibernate3.HibernateTemplate] [急切刷新 Hibernate 会话] [org.hibernate.event.def.AbstractFlushingEventListener] [刷新会话] [org.hibernate.event.def.AbstractFlushingEventListener] [处理刷新时间级联] [org.hibernate.engine.Cascade] [处理级联 ACTION_SAVE_UPDATE 为:父级] [org.hibernate.engine.CascadingAction] [级联到 saveOrUpdate: Child] [org.hibernate.event.def.AbstractSaveEventListener] [持久实例:子] [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [忽略持久实例] [org.hibernate.event.def.DefaultSaveOrUpdateEventListener] [对象已与会话关联:[Child#14139470]] [org.hibernate.engine.Cascade] [完成处理级联 ACTION_SAVE_UPDATE 为:父] [org.hibernate.event.def.AbstractFlushingEventListener] [脏检查集合] [org.hibernate.event.def.AbstractFlushingEventListener] [刷新实体和处理引用的集合] [org.hibernate.persister.entity.AbstractEntityPersister] [Child.parent 很脏] [org.hibernate.event.def.DefaultFlushEntityEventListener] [更新实体:[Child#14139470]] [org.hibernate.event.def.AbstractFlushingEventListener] [处理未引用的集合] [org.hibernate.event.def.AbstractFlushingEventListener] [调度集合删除/(重新)创建/更新] [org.hibernate.event.def.AbstractFlushingEventListener] [刷新:2 次插入,1 次更新,0 次删除到 2 个对象] [org.hibernate.event.def.AbstractFlushingEventListener] [刷新:0 次(重新)创作,0 次更新,0 次删除到 0 次收藏] [org.hibernate.pretty.Printer] [列出实体:] [org.hibernate.pretty.Printer] [父{child=Child#14139470, parentId=14139460}] [org.hibernate.pretty.Printer] [子{parent=Parent#14139460, childId=14139470}] [org.hibernate.event.def.AbstractFlushingEventListener] [执行刷新] [org.hibernate.jdbc.ConnectionManager] [注册刷新开始] [org.hibernate.persister.entity.AbstractEntityPersister] [插入实体:[Child#14139470]] [org.hibernate.jdbc.AbstractBatcher] [即将开启PreparedStatement(开启PreparedStatements:0,全局:0)] [org.hibernate.jdbc.ConnectionManager] [打开JDBC连接] [org.hibernate.SQL] [插入 CHILD_TABLE (PARENT_ID, CHILD_ID) 值 (?, ?)] [org.hibernate.jdbc.AbstractBatcher] [准备声明] [org.hibernate.persister.entity.AbstractEntityPersister] [脱水实体:[Child#14139470]] [org.hibernate.type.LongType] [将 null 绑定到参数:1] [org.hibernate.type.LongType] [绑定'14139470'到参数:2] [org.hibernate.persister.entity.AbstractEntityPersister] [插入实体:[Parent#14139460]] [org.hibernate.jdbc.AbstractBatcher] [执行批处理大小:1] [org.hibernate.jdbc.AbstractBatcher] [即将关闭PreparedStatement(打开PreparedStatements:1,全局:1)] [org.hibernate.jdbc.AbstractBatcher] [结束声明] [DEBUG] [org.hibernate.util.JDBCExceptionReporter] [无法执行 JDBC 批量更新 [插入 CHILD_TABLE (PARENT_ID, CHILD_ID) 值 (?, ?)]] java.sql.BatchUpdateException: ORA-01400: 无法将 NULL 插入 ("CHILD_TABLE"."PARENT_ID")

在前两行粗体中,父母知道孩子的 id,反之亦然。但是,在生成孩子的sql时,它会将父母的id设置为null。怎么回事?

编辑: 作为参考,我正在使用以下休眠罐子: org.hibernate:hibernate-annotations:jar:3.3.1.GA org.hibernate:hibernate:jar:3.2.6.ga

【问题讨论】:

    标签: java hibernate


    【解决方案1】:

    我不知道你为什么会出现这种行为,但我知道你在这里没有双向的一对一关联。

    您拥有的是两个不同的一对一单向关联:父级使用子表的外键了解其子级,子级使用父表的外键了解其父级。

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

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

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

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

    【讨论】:

    • 不幸的是,我坚持使用 db 结构。如果我所拥有的是两个不同的一对一单向关联,那么这就是我必须处理的。我按原样尝试了您的 java 修改(不修改表),并收到与以前相同的错误。
    • 尝试将Child实体中JoinColumn注解的可空属性设置为false,告诉Hibernate该列禁止空值。
    • 当我将 nullable=false 添加到子实体的 JoinColumn 注释时,休眠生成的 sql 现在甚至不会尝试保留 parent_id。这仍然会导致错误,因为该列在 db 中定义为不可为空。
    • 如果删除级联并显式保存两个实体会怎样?您是否尝试过更新版本的 Hibernate?
    • 我将 hibernate 和 hibernate-annotations 更新为 3.5.6-Final(这需要我删除对 j​​avax.persistence:persistence-api 的依赖并添加对 javassist:javassist 的依赖)。我仍然收到同样的错误。
    【解决方案2】:

    我修复了它,但我不确定它为什么会起作用。我将级联从父级移动到子级并保存子级而不是父级。

    在父类的注释类中,我有:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-26
      • 2011-04-30
      • 1970-01-01
      • 2015-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多