read-Atleap-10-主业务分析-ContentField类-Hibernate多对一关系实施案例

博客围绕新闻主业务展开,介绍了新闻实体的数据结构,包括NewsItem、ContentField等类的关系。详细阐述了Hibernate中多对一、一对多关系的映射配置,如级联删除设置,还对比了关联维护方不同时的性能差异,指出由多的一方维护关联更高效。

v 新闻主业务

Ø 数据结构描述

ª 通过继承PageLocalizableNewsItem完成新闻实体

    ª 通过继承、映射和引用NewsItem并不真正的保存新闻信息

    ª ContentFieldVale保存真正的新闻内容,包括标题、注释和内容体等

    ª ContentField保存布局信息,和ContentFieldVale形成引用关系,完成显示布局和内

    容的分离

Ø新闻实体类图

Ø基类Localizable分析(总览)

    ª Localizable类的映射文件分析

    ª Localizable类是Page类的父类,而Page又是NewsItem的父类。localizable表的主键是

       id

    ª ContentField类和Localizable类构成多对一的关系,代表一个新闻实体(NewsItem)中

      具有多个新闻实体元素(ContentField)ContentField类是维护关系的主控

      方。Field表的主键是id以外键localizable_idlocalizable表关联,对应localizable

表的主键:id。关联键值在被控方设置

ª LocalizableContentField的关系:

    一个新闻实体(NewsItem)中具有多个新闻实体元素(ContentField)

    如:一条新闻由标题(title)、描述(description)、内容(body)、头(head)、尾(fooder)

    等组成。

ContentField中记录一条新闻实体具有哪些元素

ContentFieldValue记录真正新闻实体元素的内容

    ªContentFieldContentFieldValue构成一对多的关系,代表一个新闻实体元素的多种表

        现形式的元素内容,如不同语言的新闻内容。

ª注;property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对应。 如

果没有指定,会使用对方关联类的主键。property-ref属性只应该用来对付老旧的数据库

系统, 可能有外键指向对方关联表的是个非主键字段(但是应该是一个惟一关键字)的

情况下。 这是一种十分丑陋的关系模型。

Ø代码具体实施

    ª ContentField类和Localizable类构成多对一的关系,ContentField类是维护关系的主控

      方。Field表的主键是id以外键localizable_idlocalizable表关联,对应localizable

表的主键:id。关联键值在被控方设置

    ª主表方对象Localizable的映射文件设置

    ª Field表的主键是id以外键localizable_idlocalizable表关联,对应localizable

表的主键:id

    ª 在这里我们指定了cascadedelete,这表示当我们删除Localizable中的记录时,自动

        对其所关联到的field表中记录实现级联删除。

    ª Localizable一对多关系映射设置,通过localizable_id关联到ContentField。代表一个

        Localizable对象含有多个ContentField对象的引用。

    ª 在设立双向关联时,关联由多对一中「多」的哪一方维护,会比由「一」的哪一方维护

        来的方便,原因请看附录

         <bag

            name="contentFields"

            lazy="true"

            inverse="true"

            cascade="delete"

        >

              <key

                  column="localizable_id"

              >

              </key>

              <one-to-many

                  class="com.blandware.atleap.model.core.ContentField"

              />

        </bag>

    ª ContentField多对一映射配置,双向设置

       <many-to-one

            name="owner"

            class="com.blandware.atleap.model.core.Localizable"

            cascade="none"

            outer-join="false"

            update="true"

            insert="true"

            access="property"

            column="localizable_id"

            not-null="true"

        />

    ª Localizable中对应的Java代码

        public abstract class Localizable extends BaseObject {

             

        /**

        * Returns fields.

         *

        * @return List

         * @hibernate.bag name="fields" inverse="true" lazy="true" cascade="delete"

        * @hibernate.collection-key column="localizable_id"

        * @hibernate.collection-one-to-many

        * class="com.blandware.atleap.model.core.ContentField"

        *注:在一方得到多方的集合

        */

        public List getContentFields() {

            return contentFields;

        }

        public void setContentFields(List contentFields) {

            this.contentFields = contentFields;

        }

    }

    ª ContentField中对应的Java代码

        /**

         * Returns the localizable owner

         *

         * @return a localizable

         * @struts.form-field

         * @hibernate.many-to-one column="localizable_id" not-null="true"

         * outer-join="false"

         *注:在多方得到一方的一个实例

        */

        public Localizable getOwner() {

            return owner;

        }

        public void setOwner(Localizable owner) {

            this.owner = owner;

        }

Ø代码具体实施

    ªContentFieldContentFieldValue构成一对多的关系,代表一个新闻实体元素的多种表

        现形式的元素内容,如不同语言的新闻内容。

    ª ContentFieldValue为多方,是主控方,维护表关系

    ª ContentField设置级联删除,即删除field中的一条记录,自动删除field_value表中的

        对应记录

    ª ContentField.hbm.xml中的关系内容

        <bag

            name="contentFieldValues"

            lazy="true"

            inverse="true"

            cascade="delete"

        >

 

              <key

                  column="field_id"

              >

              </key>

              <one-to-many

                  class="com.blandware.atleap.model.core.ContentFieldValue"

              />

        </bag>

    ª ContentFieldValue.hbm.xml中的对应内容

         <many-to-one

            name="contentField"

            class="com.blandware.atleap.model.core.ContentField"

            cascade="none"

            outer-join="auto"

            update="true"

            insert="true"

            access="property"

            column="field_id"

            not-null="false"

        />

    ª ContentField.java中的内容

    /**

     * Returns the list of contentFieldValues dependent on locale.

     *

     * @return List

     * @hibernate.bag name="contentFieldValues" inverse="true" lazy="true"

     * cascade="delete"

     * @hibernate.collection-key column="field_id"

     * @hibernate.collection-one-to-many

     * class="com.blandware.atleap.model.core.ContentFieldValue"

     */

    public List getContentFieldValues() {

        return contentFieldValues;

    }

    public void setContentFieldValues(List contentFieldValues) {

        this.contentFieldValues = contentFieldValues;

    }

    ª ContentFieldValue.java中的内容

    /**

     * Returns the field

     *

     * @return a field

     * @hibernate.many-to-one column="field_id" not-null="false"

     */

    public ContentField getContentField() {

        return contentField;

    }

    public void setContentField(ContentField contentField) {

        this.contentField = contentField;

    }

注:引用

引用地址:http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=833

ª hibernate cascade描述

如果将对象之间的关联想象为一个树形图,从某一个持久化物件为树根出发,父节点若是

持久化对象,则被父节点参考到的子节点应自动持久化,而另一方面,如果有一子节点没

办法藉由任何的父节点来参考至它,则它没有被持久化的需求,它应从数据库中加以删除。

预设上cascadenone,也就是不进行自动持久化。

ª 多对一关系property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对

应。 如果没有指定,会使用对方关联类的主键。

property-ref属性只应该用来对付老旧的数据库系统, 可能有外键指向对方关联表的是个

非主键字段(但是应该是一个惟一关键字)的情况下。 这是一种十分丑陋的关系模型。

 

ª 基本上就数据的储存来说,这样就已经足够,但这样的设计会有效能问题,显然的,这个程序将RoomUser之间的关联交由Room来维持,就Room而言,它要先储存自已,然后储存其所包括的多个User,之后再对每一个User更新(update)对自己(Room)的关联,具体而言,这个程序必须实行以下的SQL

Hibernate: insert into ROOM (address, ROOM_ID) values (?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?)

Hibernate: update USER set ROOM_ID=? where USER_ID=?

Hibernate: update USER set ROOM_ID=? where USER_ID=? 就Room而言,它并不知道其所包括的User是不是一个已储存的对象,或者即使为已储存对象,也不知道USER表格上的ROOM_ID是不是参考至ROOM表格的ROOM_ID上,所以它必须针对自己所包括的User对象一个个进行更新,以确保USER表格上的ROOM_ID是指向自己。

 

 如果将关联的维护交给User的话会比较容易,因为每个User都对应至一个Room,在储存时并用像Room一样必须对Set中的每个对象作检查,为了将关联的维护交给User,我们可以在Room.hbm.xml中的<set>修改,加上inverse="true",表示将关联的维护「反过来」交给User作:

 

Room.java

<set name="users" table="USER" inverse="true" cascade="all">

            <key column="ROOM_ID"/>

            <one-to-many class="onlyfun.caterpillar.User"/>

        </set> 由于将关联的维护交给User来作了,所以我们必须在储存时,明确的将Room设定给User,也就是说,必须这样作:

 

/*

 * 因为有user维护关联,所以必须呼叫setRoom

 */

user1.setRoom(room);

user2.setRoom(room);

      

room.getUsers().add(user1);

room.getUsers().add(user2); 这比不加上inverse="true"设定时多了个指定的动作,您必须多键几个字,所带来的是效率上的增加,Hibernate的持久层管理员会先储存Room,然后储存User,如此就可以省去之前再进行更新的动作,具体来说,就是会执行以下的SQL

 

Hibernate: insert into ROOM (address, ROOM_ID) values (?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?)

Hibernate: insert into USER (NAME, ROOM_ID, USER_ID) values (?, ?, ?) 与先前不同的是,由于关联交给了User维护,所以这次Room不用一一更新USER以确定每个ROOM_ID都指向自已。

 

 如果指定了inverse="true",而不确实的将Room指定给User会如何?那么UserRoom会各自储存,但彼此没有关联,也就是User将不会参考至RoomUSER表格的ROOM_ID将为null,例如:

 

mysql> select * from USER;

+---------+------+---------+

| USER_ID | NAME | ROOM_ID |

+---------+------+---------+

|       1 | bush |    NULL |

+---------+------+---------+

 

mysql> select * from ROOM;

+---------+------------+

| ROOM_ID | address    |

+---------+------------+

|       1 | NTU-M8-419 |

+---------+------------+ 

作个总结,在设立双向关联时,关联由多对一中「多」的哪一方维护,会比由「一」的哪一方维护来的方便,在Hibernate可以藉由inverse来设定,不设定inverse基本上也可以运行,但是效能会较差。

 设定了inverse,必须要明确的设定双方的参考,以这个主题的例子,Room要设定给User,而 "User" 也要知道Room的存在,这比不设定inverse需要键入较多的字,但从另一方面,比较符 合程序设计的直觉(单看UserRoom类别,两者要互相参考时,本来就要明确设定)。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值