关系数据库系统本身就比较复杂,加上Hibernate的O/R映射层,复杂度加重了,很容易出现问题,本人将最近遇到的问题和解决方法做一个总结,整理在下面的一系列文章中
- 正确理解Hibernate的聚合类型(collection)的使用
- 在Hibernate中正确实现关联关系中的级联操作(cascading)
- 在Hibernate框架中编写持久对象类实现外键关联的几点注意事项
- 使用Hibernate框架实现复杂的关联关系时容易出现的异常
Hibernate将表进行对象封装,在其帮助下,在面向对象编程环境中,程序员会逐渐忘掉数据库的存在。但是,如果数据库结构比较复杂,忘记数据库的存在经常引起很多异常。此文,我们对下面几个异常的处理方法进行总结:
异常1:
object references an unsaved transient instance -
save the transient instance before flushing: xxx类
save the transient instance before flushing: xxx类
异常2:
not-null property references a null or transient value:: xxx类引用xxx属性
这两个异常最容易出现在多个表之间有外键约束的情况下。例如,表A, B, C之间,C有外键分别引用A和B。A到C是1:n关系,在A的hbm文件中,用一个Set存储所有相关的C;B到C也是1:n关系,在B中也有一个Set存储相关的C。如果程序中产生了这三个类的对象,而且在内存中对象的关联关系已经成功建立了,假设C的对象先进行持久化,必然引起上述异常。
读者也许会说:这个问题太显而易见了,自然会避免的。事实上,当长时间忘记数据库的存在的时候,这个问题会时不时的出现,尤其是使用了Hibernate级联持久化技术后,很容易犯错误。我们在在Hibernate中正确实现关联关系中的级联操作(cascading)一文中描写了一种理想场景,在理想情况下,我们确定了级联树的根,只对其进行存储操作,Hibernate完成级联操作。在复杂的现实面前,我们需要区别对待
针对上述情形,我更喜欢下面的编程模式
- A和B的对象创建后即进行持久化操作
- 创建C对象后,设定了C到A和B的引用关系后,将其持久化
- 编写各种复杂的业务逻辑,期间可能对A, B, C的对象的内容和关系进行了修改
- 执行Hibernate session.flush()操作,将内存中的修改同步到数据库