摘要:前面几篇文章简单的介绍了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_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; }
}
}
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; }
}
}
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 );
}
}
/// 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 >
< 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
}
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();
}
}
}
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();
}
}
}
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();
}
}
}
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; }
}
[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);
}
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; }
}
[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();
}
}
}
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关联映射。
本文转自lihuijun51CTO博客,原文链接:
http://blog.51cto.com/terrylee/67660
,如需转载请自行联系原作者