您的位置:首页 > 其它

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

2005-09-09 14:35 351 查看
v[/b] [/b]新闻主业务
[/b]Ø[/b] [/b]数据结构描述
[/b]ª 通过继承Page和Localizable,NewsItem完成新闻实体
ª 通过继承、映射和引用NewsItem并不真正的保存新闻信息
ª ContentFieldVale保存真正的新闻内容,包括标题、注释和内容体等
ª ContentField保存布局信息,和ContentFieldVale形成引用关系,完成显示布局和内
容的分离
Ø[/b]新闻实体类图[/b][/b]


[/b]
[/b]Ø[/b]基类Localizable分析(总览)[/b]
[/b] ª 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属性只应该用来对付老旧的数据库
系统, 可能有外键指向对方关联表的是个非主键字段(但是应该是一个惟一关键字)的
情况下。 这是一种十分丑陋的关系模型。
Ø[/b]代码具体实施
[/b] ª 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 {
… …
/**
* 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;
}
Ø[/b]代码具体实施
[/b] ª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中的内容
/**
* 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描述
如果将对象之间的关联想象为一个树形图,从某一个持久化物件为树根出发,父节点若是
持久化对象,则被父节点参考到的子节点应自动持久化,而另一方面,如果有一子节点没
办法藉由任何的父节点来参考至它,则它没有被持久化的需求,它应从数据库中加以删除。
预设上cascade是none,也就是不进行自动持久化。
ª 多对一关系property-ref: (可选) 指定关联类的一个属性,这个属性将会和本外键相对
应。 如果没有指定,会使用对方关联类的主键。[/b]
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,也就是说,必须这样作:

/*
* 因为有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会如何?那么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基本上也可以运行,但是效能会较差。
[/b] 设定了inverse,必须要明确的设定双方的参考,以这个主题的例子,Room要设定给User,而 "User" 也要知道Room的存在,这比不设定inverse需要键入较多的字,但从另一方面,比较符 合程序设计的直觉(单看User与Room类别,两者要互相参考时,本来就要明确设定)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: