主要內容
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關聯映射。