天天看点

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

v 新闻主业务 Ø 数据结构描述

ª 通过继承Page和Localizable,NewsItem完成新闻实体

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

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

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

    容的分离

Ø 新闻实体类图
read-Atleap-10-主业务分析-ContentField类-Hibernate多对一关系实施案例
Ø 基类Localizable分析(总览)

    ª Localizable类的映射文件分析

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

       id

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

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

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

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

ª Localizable和ContentField的关系:

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

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

    等组成。

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

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

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

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

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

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

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

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

Ø 代码具体实施

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

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

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

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

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

表的主键:id。

    ª 在这里我们指定了cascade为delete,这表示当我们删除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 {

            …  …

        public List getContentFields() {

            return contentFields;

        }

        public void setContentFields(List contentFields) {

            this.contentFields = contentFields;

        }

    }

    ª ContentField中对应的Java代码

        public Localizable getOwner() {

            return owner;

        }

        public void setOwner(Localizable owner) {

            this.owner = owner;

        }

Ø 代码具体实施

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

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

    ª 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中的内容

    public List getContentFieldValues() {

        return contentFieldValues;

    }

    public void setContentFieldValues(List contentFieldValues) {

        this.contentFieldValues = contentFieldValues;

    }

    ª ContentFieldValue.java中的内容

    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描述

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

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

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

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

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

应。

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

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

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

ª 基本上就数据的储存来说,这样就已经足够,但这样的设计会有效能问题,显然的,这个程序将Room与User之间的关联交由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,也就是说,必须这样作:

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会如何?那么User与Room会各自储存,但彼此没有关联,也就是User将不会参考至Room,USER表格的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需要键入较多的字,但从另一方面,比较符 合程序设计的直觉(单看User与Room类别,两者要互相参考时,本来就要明确设定)。