程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Castle ActiveRecord學習實踐(4) 實現One-Many關系的映射

Castle ActiveRecord學習實踐(4) 實現One-Many關系的映射

編輯:關於.NET

主要內容

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關聯映射。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved