hibernate复杂映射关系下的删除操作和list类型映射的注意点

映射文件:

为了节省篇幅,映射文件中一些不重要的信息都被省略

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);
    • 若上面两个属性中任一个设置为非"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);
    • 继承映射的子类可以重复父类的映射字段,但最好不要这样做.
  • 关于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小节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值