NHibernate文档翻译 第7章----示例: Parent/Child
来源:互联网 发布:网络编辑没经验能做吗 编辑:程序博客网 时间:2024/06/07 04:50
目录
- 关于collections
- 双向的一对多关系(Bidirectional one-to-many)
- 级联生命周期(Cascading lifecycle)
- 级联更新(Using cascading
update()
) - 结论
刚刚接触NHibernate的人大多是从父子关系(parent / child type relationship)的建模入手的。父子关系的建模有两种方法。比较简便、直观的方法就是在实体类Parent和Child之间建立 <one-to-many>
的关联关系,从Parent
指向Child
,对新手来说尤其如此。但还有另一种方法,就是将Child声明为一个<composite-element>
(组合元素)。可以看出在NHibernate中使用一对多关联比composite element更接近于通常parent / child关系的语义。下面我们会阐述如何使用双向可级联的一对多关联(bidirectional one to many association with cascades)去建立有效、优美的parent / child关系。这一点也不难!
在NHibernate下,实体类将collection作为自己的一个逻辑单元,而不是被容纳的多个实体。这非常重要!它主要体现为以下几点:
-
当删除或增加collection中对象的时候,拥有这个collection的实体对象的版本值会递增。
-
如果一个从collection中移除的对象是一个值类型(value type)的实例,比如composite element,那么这个对象的持久化状态将会终止,其在数据库中对应的记录会被删除。同样的,向collection增加一个value type的实例将会使之立即被持久化。
-
另一方面,如果从一对多或多对多关联的collection中移除一个实体( 一对多one-to-many 或者 多对多many-to-many关联),在缺省情况下这个对象并不会被删除。这个行为是完全合乎逻辑的--改变一个实体的内部状态不应该使与它关联的实体消失掉!同样的,向collection增加一个实体不会使之被持久化。
实际上,向Collection增加一个实体的缺省动作只是在两个实体之间创建一个连接而已,同样移除的时候也只是删除连接。这种处理对于所有的情况都是合适的。不适合所有情况的其实是父子关系本身,因为子对象是否存在依赖于父对象的生存周期。
让我们从一个简单的例子开始,假设要实现一个从类Parent
到类Child
的一对多关系。
<set name="Children"> <key column="parent_id" /> <one-to-many class="Child" /></set>
如果我们运行下面的代码
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();p.Children.Add( c );session.Save( c );session.Flush();
NHibernate就会产生下面的两条SQL语句:
-
一条
INSERT
语句,用于创建对象c
对应的数据库记录 -
一条
UPDATE
语句,用于创建从对象p
到对象c
的连接
这样做不仅效率低,而且违反了列parent_id
非空的限制。
底层的原因是,对象p
到对象c
的连接(外键parent_id
)没有被当作是Child
对象状态的一部分,也没有在INSERT
的时候被创建。解决的办法是,在Child
一端设置映射。
<many-to-one name="Parent" column="parent_id" not-null="true"
(我们还需要为类Child
添加Parent
属性)
现在实体Child
在管理连接的状态,为了使collection不更新连接,我们使用inverse
属性。
<set name="Children" inverse="true"> <key column="parent_id" /> <one-to-many class="Child" /></set>
下面的代码是用来添加一个新的Child
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();c.Parent = p;p.Children.Add( c );session.Save( c );session.Flush();
现在,只会有一条INSERT
语句被执行!
为了让事情变得井井有条,可以为Parent
加一个AddChild()
方法
public void AddChild( Child c ) { this.Children.Add( c ); c.Parent = this;}
AddChild
把代码简化了
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();p.AddChild( c ); // session.Save( c );session.Flush();
对每个对象调用Save()
()方法很麻烦,我们可以用级联来解决这个问题。
<set name="Children" inverse="true" cascade="all"> <key column="parent_id" /> <one-to-many class="Child" /></set>
配置级联以后,代码就简化了:
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = new Child();p.AddChild( c );session.Flush();
级联十分依赖unsaved-value
属性(attribute)。请确保<id>
属性(property)的默认值和unsaved-value
一样。
类似的,当保存或删除Parent
时我们不需要遍历children。下面的代码从数据库中删除了p
和它所有的children。
Parent p = session.Load( typeof( Parent ), pid ) as Parent;session.Delete( p );session.Flush();
然而,这段代码
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = null;foreach( Child child in p.Children ) { c = child; // only care about first Child break;}p.Children.Remove( c );c.Parent = null;session.Flush();
不会从数据库删除c
;它只会删除与p
之间的连接(并且会导致违反NOT NULL
约束,在这个例子中)。你需要明确调用Child
的Delete()
方法。
Parent p = session.Load( typeof( Parent ), pid ) as Parent;Child c = null;foreach( Child child in p.Children ) { c = child; // only care about first Child break;}p.Children.Remove( c );c.Parent = null;session.Delete( c );session.Flush();
在我们的例子中,如果我们规定没有父对象的话,子对象就不应该存在,如果将子对象从collection中移除,实际上我们是想删除它。要实现这种要求,就必须使用cascade="all-delete-orphan"
。
<set name="Children" inverse="true" cascade="all-delete-orphan"> <key column="parent_id" /> <one-to-many class="Child" /></set>
注意:即使在collection一方的映射中指定iinverse="true"
,在遍历collection的时候级联操作仍然会执行。如果你想要通过级联进行子对象的插入、删除、更新操作,就必须把它加到collection中,只调用Child.Parent
的setter是不够的。
update()
) 假设我们从ISession
中装入了一个Parent
对象,用户界面对其进行了修改,然后我们希望在一个新的ISession里面调用 Update()
来更新它。对象Parent
包含了子对象的集合,由于打开了级联更新,NHibernate需要知道哪些子对象是新的,哪些是数据库中已经存在的。我们假设Parent
和Child
对象的标识属性的类型为System.Int32
。NHibernate会使用标识属性的值来判断哪些子对象是新的。(你也可以使用version 或 timestamp 属性)
unsaved-value
属性是用来表示新实例的标识属性值的,缺省为"null",对于.net的值类型(ValueTypes)这不是一个好的默认值,所以你需要提供unsaved-value
。
如果我们使用原始类型作为标识类型的话,我们在配置Child类映射的时候就必须写:
<id name="Id" type="Int64" unsaved-value="0">
对于Child
映射. (也有unsaved-value
属性(attribute)提供给版本(version)和时间戳(timestamp)属性(property)映射)
下面的代码会更新parent
和child
对象,并且插入newChild
对象。
//parent and child were both loaded in a 上一页ious sessionparent.AddChild( child );Child newChild = new Child();parent.AddChild( newChild );session.Update( parent );session.Flush();
好的,对于自动生成标识的情况这样做很方便,但是自分配的标识和复合标识怎么办呢?这是有点麻烦,因为unsaved-values
无法区分新对象(标识是用户指定的)和前一个ISession
装入的对象。在这种情况下,你可能需要给NHibernate一些提示,在调用update(parent)之前:
-
在这个类的
<version>
or<timestamp>
属性映射上定义unsaved-value="null"
或者unsaved-value="negative"
。 -
在对父对象执行
Update( parent )
之前,设定unsaved-value="none"
并且显式的调用Save()
在数据库创建新子对象 -
在对父对象执行
Update( parent )
之前,设定unsaved-value="any"
并且显式的调用Update()
更新已经装入的子对象
none
是自分配标识和复合标识的unsaved-value
的缺省值。
这个问题往往让新手感到迷惑,它确实不太容易消化。不过,经过一些实践以后,你会感觉越来越顺手。父子对象模式已经被广泛的应用在NHibernate应用程序中。
在第一段中我们曾经提到另一个方案。复合元素的语义与父子关系是等同的,但是我们并没有详细讨论。很不幸复合元素还有两个重大限制:复合元素不能拥有collections,并且,除了用于惟一的父对象外,它们不能再作为其它任何实体的子对象。(但是,通过使用 <idbag>
映射,它们可能拥有代理主键。)
- NHibernate文档翻译 第7章----示例: Parent/Child
- NHibernate文档翻译 第11章----Nullables
- NHibernate文档翻译 第10章----NHibernate.Tool.hbm2net
- NHibernate文档翻译 第8章----NHibernate缓存(NHibernate.Caches)
- NHibernate文档翻译 第1章----快速起步
- NHibernate文档翻译 第2章---- ISessionFactory配置
- NHibernate文档翻译 第4章----O/R Mapping基础
- NHibernate文档翻译 第5章----集合类(Collections)映射
- NHibernate文档翻译 第6章----关联映射
- NHibernate文档翻译 第9章----使用AttributesNHibernate.Mapping.Attributes
- NHibernate文档翻译
- NHibernate文档翻译 第3章----持久化类(Persistent Classes)
- 示例:父子关系(Parent Child Relationships)
- 2-7parent > child选择器
- parent > child
- hadoop2.7文档翻译第一天
- SQLAlchemy文档翻译第1章
- 【jQuery】parent > child选择器
- NHibernate文档翻译 第3章----持久化类(Persistent Classes)
- NHibernate文档翻译 第4章----O/R Mapping基础
- NHibernate文档翻译 第5章----集合类(Collections)映射
- NHibernate文档翻译 第6章----关联映射
- Xp系统IIS服务器经常出现:目前访问网站的用户过多--该如何解决
- NHibernate文档翻译 第7章----示例: Parent/Child
- NHibernate文档翻译 第8章----NHibernate缓存(NHibernate.Caches)
- NHibernate文档翻译 第9章----使用AttributesNHibernate.Mapping.Attributes
- C++沉思录读书笔记1.如何定义一个完整的类
- NHibernate文档翻译 第10章----NHibernate.Tool.hbm2net
- 正则表达式小谈
- NHibernate文档翻译 第11章----Nullables
- perl需要的模块全集
- NHibernate文档写作计划(草稿) [转]