摘要:前面几篇文章简单的介绍了ActiveRecord 中的基本映射以及构建配置信息,本文我们用ActiveRecord 里面的Blog ,Post 例子来实现One-Many/Many-One 关联。
主要内容
1 .准备数据表结构
2 .编写实体类并介绍HasMany 和BlongsTo 特性
3 .构建配置信息
4 .编写测试代码
一.准备数据表结构
在这个例子中,我们引入了两个对象Blog 、Post ,它们之间的关系是一对多,即一个Blog 有多篇Post 。需要用到的数据表结构如下
CREATE
TABLE
Blogs ( blog_id
int
IDENTITY
(
1
,
1
)
PRIMARY
KEY
, blog_name
varchar
(
50
), blog_author
varchar
(
50
) )
CREATE
TABLE
Posts ( post_id
int
IDENTITY
(
1
,
1
)
PRIMARY
KEY
, post_title
varchar
(
50
), post_contents
text
, post_categories
varchar
(
50
), post_blogid
int
FOREIGN
KEY
REFERENCES
Blogs (blog_id), post_created
datetime
, post_published
bit
)
二.编写实体类
首先我们来看Blog 实体类的编写,需要用到HasMany 特性,这时我们会在Blog 实体类中定义一个Posts 属性,用它来表示该Blog 所发表的所有Posts ,代码如下
[ActiveRecord(
"
Blogs
"
)]
public
class
Blog : ActiveRecordBase
{ // …… private IList _posts; [HasMany( typeof (Post), Table = " posts " , ColumnKey = " post_blogid " )] public IList Posts { get { return _posts; } set { _posts = value; } } }
HasManyAttribute说明
属性
说明
示例
Cascade
指明哪些操作会从父对象级联到关联的对象,相关的操作见后面,如果不指定,则为None
Cascade= ManyRelationCascadeEnum. All
Inverse
指定是否级联操作
Inverse = true |false
Schema
指定Schema 的名字
Schema= "ARDemo"
Table
指定持久化类所关联的数据库表名,如果表名与类名相同,可以省略
Table= "posts"
ColumnKey
指定关联类的一个属性,这个属性将会和本外键相对应。
ColumnKey= "post_blogid"
Where
指定一个附加SQL 的Where 子句
Where= "IsPost = 0"
Lazy
指定是否延迟加载关联对象
Lazy= true |false
Cascade
的类型值有如下几种
类型
说明
None
不进行级联操作
SaveUpdate
进行级联Save/Update 操作
Delete
进行级联Delete 操作
All
进行级联Save/Update/Delete 操作
AllDeleteOrphan
进行级联Save/Update/Delete 操作,并删除无相关父对象的子对象
在Post 实体类中,我们需要定义一个Blog 类型的属性,并且用到BlongsTo 特性,即一个Post 属于某一个Blog ,代码如下:
[ActiveRecord(
"
Posts
"
)]
public
class
Post : ActiveRecordBase
{ // …… private Blog _blog; [BelongsTo( " blogid " )] public Blog Blog { get { return _blog; } set { _blog = value; } } }
BelongsToAttribute说明
属性
说明
示例
Cascade
指定级联操作
Cascade= CascadeEnum. SaveUpdate
Column
列名,外键字段名
BelongsTo("blogid" )
Column= "blogid"
Insert
是否允许增加
Insert= true |false
NotNull
是否允许为空
NotNull = true |false
OuterJoin
是否允许外连接抓取
OuterJoin= OuterJoinEnum. True
Unique
是否唯一
Unique = true |false
Update
是否允许更新
Update = true |false
Cascade 类型如下
选项
说明
None
默认值,不进行级联操作
All
进行级联Save/Update/Delete 操作
SaveUpdate
进行级联Save/Update 操作
Delete
进行级联Delete 操作
OuterJoin 选项有三个:Auto ,True ,False
最后完整的Blog 实体类如下
/**/
/// <summary> /// Blog 的摘要说明。 /// </summary>
[ActiveRecord(
"
Blogs
"
)]
public
class
Blog : ActiveRecordBase
{ private int _id; private String _name; private String _author; private IList _posts; [PrimaryKey(PrimaryKeyType.Native, " blog_id " )] public int Id { get { return _id; } set { _id = value; } } [Property(" blog_name " )] public String Name { get { return _name; } set { _name = value; } } [Property(" blog_author " )] public String Author { get { return _author; } set { _author = value; } } [HasMany(typeof (Post), Table = " posts " , ColumnKey = " post_blogid " )] public IList Posts { get { return _posts; } set { _posts = value; } } public static void DeleteAll() { DeleteAll( typeof (Blog) ); } public static Blog[] FindAll() { return (Blog[]) FindAll( typeof (Blog) ); } public static Blog Find( int id) { return (Blog) FindByPrimaryKey( typeof (Blog), id ); } }
完整的Post 类如下
/**/
/// <summary> /// Post 的摘要说明。 /// </summary>
[ActiveRecord(
"
Posts
"
)]
public
class
Post : ActiveRecordBase
{ private int _id; private String _title; private String _contents; private String _category; private DateTime _created; private bool _published; private Blog _blog; public Post() { _created = DateTime.Now; } public Post(Blog blog, String title, String contents, String category) : this () { _blog = blog; _title = title; _contents = contents; _category = category; } [PrimaryKey(PrimaryKeyType.Native," post_id " )] public int Id { get { return _id; } set { _id = value; } } [Property(" post_title " )] public String Title { get { return _title; } set { _title = value; } } [Property(Column= " post_contents " ,ColumnType = " StringClob " )] public String Contents { get { return _contents; } set { _contents = value; } } [Property(" post_categories " )] public String Category { get { return _category; } set { _category = value; } } [BelongsTo(" post_blogid " )] public Blog Blog { get { return _blog; } set { _blog = value; } } [Property(" post_created " )] public DateTime Created { get { return _created; } set { _created = value; } } [Property(" post_published " )] public bool Published { get { return _published; } set { _published = value; } } public static void DeleteAll() { ActiveRecordBase.DeleteAll( typeof (Post) ); } public static Post[] FindAll() { return (Post[]) ActiveRecordBase.FindAll( typeof (Post) ); } }
三.构建配置信息
这里我采用上篇中将过的XML 配置方式
<?
xml version="1.0" encoding="utf-8"
?>
<
activerecord
>
<
config
>
<
add
key
="hibernate.connection.driver_class"
value
="NHibernate.Driver.SqlClientDriver"
/>
<
add
key
="hibernate.dialect"
value
="NHibernate.Dialect.MsSql2000Dialect"
/>
<
add
key
="hibernate.connection.provider"
value
="NHibernate.Connection.DriverConnectionProvider"
/>
<
add
key
="hibernate.connection.connection_string"
value
="Data Source=.;Initial Catalog=test;Integrated Security=SSPI"
/>
</
config
>
</
activerecord
>
四.编写测试代码
1 .级联增加:新增一个Blog ,并同时添加相关的Post 到数据表中
[Test]
public
void
TestCascadingSave()
{ // 创建Blog对象 Blog blog = new Blog(); blog.Name = " Terrylee's Tech Space " ; blog.Author = " Terrylee " ; // 执行事务,持久化对象到数据库 using (TransactionScope tran = new TransactionScope()) { try { blog.Create(); for ( int i = 1 ;i < 11 ;i ++ ) { // 创建Post对象 Post post = new Post(); post.Title = " This is my " + i.ToString() + " post " ; post.Category = " Castle " ; post.Blog = blog; post.Save(); } tran.VoteCommit(); } catch { tran.VoteRollBack(); } }// The changes will be sent to the DB when the session is disposed here }
2
.级联更新:
首先我们为一个已经存在的Blog 增加多个Post
[Test]
public
void
TestCascadingUpdate()
{ // 找到ID为5的Blog Blog blog = Blog.Find( 5 ); // 执行事务,持久化对象到数据库 using (TransactionScope tran = new TransactionScope()) { try { for ( int i = 1 ;i < 5 ;i ++ ) { // 创建Post对象 Post post = new Post(); post.Title = " This is my " + i.ToString() + " post " ; post.Category = " Castle " ; post.Save(); // 注意这句 blog.Posts.Add(post); } blog.Update(); tran.VoteCommit(); } catch { tran.VoteRollBack(); } } }
当然上面的更新代码也可以这样去写:
[Test]
public
void
TestCascadingUpdate()
{ // 找到ID为5的Blog Blog blog = Blog.Find( 5 ); // 执行事务,持久化对象到数据库 using (TransactionScope tran = new TransactionScope()) { try { for ( int i = 1 ;i < 5 ;i ++ ) { // 创建Post对象 Post post = new Post(); post.Title = " This is my " + i.ToString() + " post " ; post.Category = " Castle " ; // 在这儿指定它们的关联 post.Blog = blog; post.Save(); } tran.VoteCommit(); } catch { tran.VoteRollBack(); } } }
但是如果我们去掉post.Save() 这句话,就会发现Post 并没有增加到库中:
[Test]
public
void
TestCascadingUpdate()
{ // 找到ID为5的Blog Blog blog = Blog.Find( 5 ); // 执行事务,持久化对象到数据库 using (TransactionScope tran = new TransactionScope()) { try { for ( int i = 1 ;i < 5 ;i ++ ) { // 创建Post对象 Post post = new Post(); post.Title = " This is my " + i.ToString() + " post " ; post.Category = " Castle " ; // 注释掉这句 // post.Save(); blog.Posts.Add(post); } blog.Update(); tran.VoteCommit(); } catch { tran.VoteRollBack(); } } }
此时,必须修改我们的Blog 类,设置级联操作为SaveUpdate ,上面的代码才可以正常执行
//
[HasMany(
typeof
(Post), Table
=
"
posts
"
, ColumnKey
=
"
post_blogid
"
,Cascade
=
ManyRelationCascadeEnum.SaveUpdate)]
public
IList Posts
{ get { return _posts; } set { _posts = value; } }
下面再测试一个删除某一个Blog 的某些Post 后,再保存
[Test]
public
void
TestCascadingUpdateDel()
{ // 找到ID为7的Blog Blog blog = Blog.Find( 7 ); int expectedCount = blog.Posts.Count; using (TransactionScope tran = new TransactionScope()) { try { blog.Posts.RemoveAt( 0 ); blog.Update(); tran.VoteCommit(); } catch { tran.VoteRollBack(); } } int actualCount = Blog.Find( 7 ).Posts.Count; Assert.AreEqual(expectedCount - 1 ,actualCount); }
上面这段代码测试可以通过,但是我们会发现表Posts 中会有一些记录没有BlogId ,修改Blog 实体类重新设置级联操作,就可以正常删除了:
//
[HasMany(
typeof
(Post), Table
=
"
posts
"
, ColumnKey
=
"
post_blogid
"
,Cascade
=
ManyRelationCascadeEnum.AllDeleteOrphan)]
public
IList Posts
{ get { return _posts; } set { _posts = value; } }
3.级联删除
删除一个Blog 对象后,对应的Post 对象应该全部删除
[Test]
public
void
TestCascadingDelete()
{ // 找到ID为7的Blog对象 Blog blog = Blog.Find( 5 ); using (TransactionScope tran = new TransactionScope()) { try { blog.Delete(); tran.VoteCommit(); } catch { tran.VoteRollBack(); } } }
同样要注意设置级联操作。
关于One-Many 关联映射就介绍这么多了,至于Many-One 关联同One-Many ,只不过对HasMany 和BlongsTo 设置的位置不一样而已,在下一篇文章中我会介绍在ActiveRecord 中实现Many-Many 关联映射。
参考资料
Castle 的官方网站http://www.castleproject.org
作者: TerryLee 出处:http://terrylee.cnblogs.com