映射文件:
为了节省篇幅,映射文件中一些不重要的信息都被省略
TClassMeta.hbm.xml
<hibernate-mapping> <class name="TClassMeta" table="T_CLASS_META"> <id name="classMetaId" type="java.lang.Long" column="CLASS_META_ID"> </id> <set name="tfieldMetas" cascade="none"> <key column="CLASS_META_ID"/> <one-to-many class="com.ebao.pub.metadata.data.TFieldMeta"/> </set> </class></hibernate-mapping>
PdsType.hbm.xml
<hibernate-mapping> <joined-subclass name="PdsType" extends="TClassMeta" table="T_CLASS_META_PDS"> <key column="CLASS_META_ID" /> <list name="pdsAttributes" inverse="true" cascade="none"> <key column="CLASS_META_ID"/> <index column="DISPLAY_ORDER"/> <one-to-many class="PdsAttribute"/> </list> </joined-subclass></hibernate-mapping>
TFieldMeta.hbm.xml
<hibernate-mapping> <class name="TFieldMeta" table="T_FIELD_META"> <id name="fieldMetaId" type="java.lang.Long" column="FIELD_META_ID"> </id> <many-to-one name="tclassMeta" column="CLASS_META_ID" /> </class></hibernate-mapping>
PdsAttribute.hbm.xml
<hibernate-mapping> <joined-subclass name="PdsAttribute" extends="TFieldMeta" table="T_FIELD_META_PDS" lazy="false"> <key column="FIELD_META_ID" /> <property name="displayOrder" type="java.lang.Integer" column="DISPLAY_ORDER" /> <property name="classMetaId" type="java.lang.Long" column="CLASS_META_ID"/> </joined-subclass></hibernate-mapping>
对象关系:
TClassMeta ------------- TFieldMeta /|\ /|\ | | PdsType ------------- PdsAttribute
关系描述:
- PdsType继承TClassMeta
- PdsAttribute继承TFieldMeta
- TClassMeta与TFieldMeta是一对多的关系
- PdsType与PdsAttribute是一对多的关系
- 继承映射的策略是:table per subclass(每个子类一个表)
值得关注的问题:
- 删除一个PdsAttribute
这是最容易引起问题的地方,而且在目前来讲,还没有任何文档涉及这种复杂情况下的删除,正确的操作应该有下面两种:- 若在父类TClassMeta的映射文件中指定tfieldMetas字段的属性cascade="none",且子类PdsType的pdsAttributes字段的属性cascade="none",则在删除是直接对pdsAttribute删除就可以了,实例代码:
PdsAttributeDelegate.remove(AttributeId);
- 若在父类TClassMeta的映射文件中指定tfieldMetas字段的属性cascade="none",且子类PdsType的pdsAttributes字段的属性cascade="none",则在删除是直接对pdsAttribute删除就可以了,实例代码:
-
- 若上面两个属性中任一个设置为非"none"的话,都要在删除pdsAttribute时,分别从TClassMeta(tfieldMetas)与PdsType(attributes)的集合中显式地删除pdsAttribute对象,否则会抛出net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade(remove deleted object from associations)异常,示例代码如下:
TClassMeta tclassMeta = pdsAttribute.getTclassMeta(); // 删除父类关系 Set tfieldMetas = tclassMeta.getTfieldMetas(); tfieldMetas.remove(TFieldMetaDelegate.load(AttributeId)); Long pdsTypeId = tclassMeta.getClassMetaId(); PdsType pdsType = PdsTypeDelegate.load(pdsTypeId); // 删除子类关系 List attributes = pdsType.getPdsAttributes(); attributes.remove(pdsAttribute); // 更新顺序号 int displayOrder = pdsAttribute.getDisplayOrder().intValue(); for (Iterator iter = attributes.iterator(); iter.hasNext();) { PdsAttribute tmp = (PdsAttribute) iter.next(); int tmpDisplayOrder = tmp.getDisplayOrder().intValue(); if (displayOrder < tmpDisplayOrder) { tmp.setDisplayOrder(new Integer(tmpDisplayOrder - 1)); } } // 根据PdsType对象的cascade属性值,这一步是可有可无的,不过最好加上来,油多不坏菜嘛:) PdsAttributeDelegate.remove(AttributeId);
- 其实上面的映射方法是值得商榷的,可以删除PdsType类中维护对PdsAttribute类的一对多引用(因为父类中已经维护了这个关系,只是映射的集合类型是list而已),也就是删除上面的PdsType映射文件的list映射内容.若没有这个映射,则示例代码如下:
PdsType pdsType = (PdsType) pdsAttribute.getTclassMeta(); // 删除关联关系 Set attributes = pdsType.getTfieldMetas(); attributes.remove(pdsAttribute); // 更新顺序号 int displayOrder = pdsAttribute.getDisplayOrder().intValue(); for (Iterator iter = attributes.iterator(); iter.hasNext();) { PdsAttribute tmp = (PdsAttribute) iter.next(); int tmpDisplayOrder = tmp.getDisplayOrder().intValue(); if (displayOrder < tmpDisplayOrder) { tmp.setDisplayOrder(new Integer(tmpDisplayOrder - 1)); } } PdsAttributeDelegate.remove(AttributeId);
- 继承映射的子类可以重复父类的映射字段,但最好不要这样做.
- 若上面两个属性中任一个设置为非"none"的话,都要在删除pdsAttribute时,分别从TClassMeta(tfieldMetas)与PdsType(attributes)的集合中显式地删除pdsAttribute对象,否则会抛出net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade(remove deleted object from associations)异常,示例代码如下:
- 关于list映射的问题,需要分外添加一个顺序字段,类型应该只要是数字类型就可以了,下面假设这个字段名为:POSITION
一般开发时都是使用set,简单一点,若使用list也有不少暗礁- POSITION一定要从0开始,若从1开始,则它会把映射出来的list对象的0设置的对象为null.推而广之,若POSITION是从2开始,则映射出来的集合对象的0,1位置的对象都是null,可能有人说,没是,我取的时候做一下检测就可以,这是不对的,若我们调用list.size()方法,出来的结果就是错误,而且还会引起更大的错误
- POSITION一定要是连续的,否则取出来的list对象会把空缺位置的对象设置为null
- POSITION不能有重复的数据,如果有相同,或者2个以上重复的序号的话,最后hibernate只会取出来的一条记录,具体是那条记录,要看产生的sql语句了,默认是物理位置的第一条
- 在进行删除操作时,hibernate会自动更新这个POSITION字段,比如现在的序号有0,1,2,3,4. 若删除2,则hibernate会自动把后面的3,4分别改为2,3.当然自己手动更新也是可以的.
注意:这个自动更新,可能会有问题,只有在集合类中删除这个对象才可以,若是单独删除这个对象,是不会自动更新POSITION的
- 关于cascade的属性设置,我个人认为在关系很复杂的对象图中,应该谨慎设置这个属性,少用all的值
参考资料:
- hibernate(2.1.6版本)自带文档的9.8小节,说得比较含糊,没有经过实践操作,很难理解它的重要性
- oreilly:Hibernate: A Developer's Notebook的5.4小节