Castle ActiveRecord学习实践(5):实现Many–Many关系的映射

摘要:多对多的关系在日常开发中也会经常遇到,在ActiveRecord中我们用HasAndBelongsToMany特性来实现Many-Many的关联,本文将通过一个具体的实例来介绍这一用法。

 

主要内容

1.准备数据库表

2.编写实体类

3.编写测试代码

 

一.准备数据库表

接着在上篇文章中的例子,为了实现多对多的关系,我们引入Community,即每个Blog可以属于多个社区,每个社区也可以有多个Blog

CREATE TABLE Blogs (

    blog_id     
int IDENTITY(11PRIMARY KEY,

    blog_name   
varchar(50),

    blog_author 
varchar(50)

)

 

CREATE TABLE Blog_Community (

     blog_Id 
int NOT NULL ,

     community_Id 
int NOT NULL 

 )

 

 
CREATE TABLE Communities (

     community_Id 
int IDENTITY (11PRIMARY KEY,

     community_Name 
varchar (50) ,

     community_Intro 
varchar (500

 )

 

二.编写实体类代码

为了实现多对多的关系,我们要在BlogCommunity类中分别使用HasAndBelongsToMany特性,不需要编写Blog_Community类。示例代码:

// Blog

[HasAndBelongsToMany( 
typeof(Community), 

        Table
="Blog_Community"

        ColumnRef
=" community_id "

        ColumnKey
=" blog_id " )]

public IList Communitys

{

    
get return _community; }

    
set { _ community = value; }

}


 

// Community

[HasAndBelongsToMany( 
typeof(Blog), 

        Table
="Blog_Community"

        ColumnRef
="blog_id"

        ColumnKey
="community_id" )]

public IList Blogs

{

    
get return _blog; }

    
set { _ blog = value; }

}


HasAndBelongsToMany的参数相信大家都能够看明白,指定关联表名和关联的外键就可以了。

注意:这三个参数必须指定,不可以省略!

HasManyAttribute说明

属性

说明

示例

Cascade

指明哪些操作会从父对象级联到关联的对象,相关的操作见后面,如果不指定,则为None

Cascade=ManyRelationCascadeEnum.All

Inverse

指定是否级联操作

Inverse =true|false

Schema

指定Schema的名字

Schema="ARDemo"

Table

指定持久化类所关联的数据库表名,如果表名与类名相同,可以省略

Table="posts"

ColumnKey

本实体类于另一个实体类关联的外键

ColumnKey="community_id"

ColumnRef

另一实体类的外键

ColumnRef="blog_id"

Where

指定一个附加SQLWhere子句

Where="IsPost = 0"

Lazy

指定是否延迟加载关联对象

Lazy=true|false

 

Cascade的类型值有如下几种

类型

说明

None

不进行级联操作

SaveUpdate

进行级联Save/Update操作

Delete

进行级联Delete操作

All

进行级联Save/Update/Delete操作

AllDeleteOrphan

进行级联Save/Update/Delete操作,并删除无相关父对象的子对象

 

最后完整的实体类如下:

/// <summary>

/// Blog 的摘要说明。

/// </summary>


[ActiveRecord(
"Blogs")]

public class Blog : ActiveRecordBase

{

    
private int _id;

 

    
private String _name;

 

    
private String _author;

 

    
private IList _community;

 

    [PrimaryKey(PrimaryKeyType.Identity, 
"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; }

    }


    

    [HasAndBelongsToMany(
typeof(Community),

            Table
="Blog_Community",

            ColumnRef
=" community_id ",

            ColumnKey
=" blog_id " )]

    
public IList Communities

    
{

        
get return _community; }

        
set { _community = 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 );

    }


}

/// <summary>

/// Community 的摘要说明。

/// </summary>


[ActiveRecord(
"Communities")]

public class Community : ActiveRecordBase

{

    
private int _id;

 

    
private String _name;

 

    
private String _intro;

 

    
private IList _blog;

 

    [PrimaryKey(PrimaryKeyType.Identity, 
"Community_Id")]

    
public int Id

    
{

        
get return _id; }

        
set { _id = value; }

    }


 

    [Property(
"Community_Name")]

    
public String Name

    
{

        
get return _name; }

        
set { _name = value; }

    }


 

    [Property(
"Community_Intro")]

    
public String Author

    
{

        
get return _intro; }

        
set { _intro = value; }

    }


    

    [HasAndBelongsToMany(
typeof(Blog),

            Table
="Blog_Community",

            ColumnRef
="blog_id",

            ColumnKey
="community_id" )]

    
public IList Blogs

    
{

        
get return _blog; }

        
set { _blog = value; }

    }


 

    
public static void DeleteAll()

    
{

        DeleteAll( 
typeof(Community) );

    }


 

    
public static Community[] FindAll()

    
{

        
return (Community[]) FindAll( typeof(Community) );

    }


 

    
public static Community Find(int id)

    
{

        
return (Community) FindByPrimaryKey( typeof(Community), id );

    }


}

 

三.编写测试代码

下面是我写的一些简单的测试代码,有兴趣的可以看一下。

1.级联增加:新增一个Blog,让它同时属于好几个社区

[Test]

public void TestCascadingSave()

{

    
//新建一个Blog

    Blog blog 
= new Blog();

    blog.Name 
= "Tech Space";

    blog.Author 
= "Terrylee";

    

    
//属于ID为1,2社区

    ArrayList list 
= new ArrayList();

    list.Add(Community.Find(
1));

    list.Add(Community.Find(
2));

    blog.Communities 
= list;

    

    
//保存

    blog.Save();

}


2.级联更新:对一个已经存在Blog,更改它属于更多的社区

[Test]

public void TestCascadingUpdate()

{

    
//测试1:查找一个Blog

    Blog blog 
= Blog.Find(10);

 

    IList clist 
= blog.Communities;

 

    clist.Add(Community.Find(
4));

    clist.Add(Community.Find(
3));

 

    blog.Save();

 

    
//测试2:查找一个Community

    Community community 
= Community.Find(3);

 

    IList blist 
= community.Blogs;

 

    blist.Add(Blog.Find(
8));

 

    community.Save();

}

 

3.级联删除:删除一个Blog,级联表中对应的记录应该删除,但Community不能删除,因为还有别的Blog和它关联

[Test]

public void TestCascadingDelete()

{

    
//测试1:删除Blog

    Blog blog 
= Blog.Find(10);

    

    
using(TransactionScope btran = new TransactionScope())

    
{

        
try

        
{

            blog.Delete();

            btran.VoteCommit();

        }


        
catch

        
{

            btran.VoteRollBack();

        }


    }


 

    
//测试2:删除Community

    Community community 
= Community.Find(3);

 

    
using(TransactionScope ctran = new TransactionScope())

    
{

        
try

        
{

            community.Delete();

            ctran.VoteCommit();

        }


        
catch

        
{

            ctran.VoteRollBack();

        }


    }


}

 

好了,关于Many-Many关联映射就写这么多了,内容比较简单。下篇文章我会介绍在ActiveRecord中实现延迟加载和使用Where子句。

 

参考资料

Castle的官方网站http://www.castleproject.org

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2006-04-10 08:17 TerryLee 阅读(7496) 评论(31)  编辑 收藏 网摘 所属分类: [11]  数据持久[12]  开源世界

  回复  引用    
#1楼2006-04-10 08:59 | ddee[未注册用户]
还行,支持
  回复  引用  查看    
#2楼2006-04-10 09:16 | windwolf      
我记得nh中m2m有一头好像要inverse, 否则取不到值(也可能是我编程有问题:))
不知在castle里是这么解决这个问题的

  回复  引用  查看    
#3楼[楼主]2006-04-10 12:52 | Terrylee      
@windwolf
Castle ActiveRecord里面也支持inverse,它只不过是对于NH做了一层封装,可能会对inverse赋了初始值!

  回复  引用  查看    
#4楼2006-05-20 09:03 | 彭伟      
@Terrylee
我加入HasAndBelongsMany属性后,那个Find()的方法一用就出错,报的是SQL语句的错误,是不是我的AR版本不对?还是配置出问题了?

  回复  引用    
#5楼2006-05-24 14:47 | zzsu[未注册用户]
看过的所有Where语句相关的示例都以HasMany说明,请教在HasAndBelongsToMany中使用Where语句,主要是使用什么前缀。
  回复  引用  查看    
#6楼2006-06-27 16:16 | 一汐      
@Terrylee
你好,最近又遇到了问题如下:
单独执行下面两句不是热核情况下都能成功
ArrayList list = new ArrayList(); list.Add(Community.Find(6));
执行前提:手动添加了Communities表的数据使community_Id能取到3、4、5(随便了)
然后为3、4社区添加blog:
Blog blog = new Blog();
blog.Name = "Tech Space";
blog.Author = "Terrylee";
//属于ID为3,4社区
ArrayList list = new ArrayList();
list.Add(Community.Find(3));
list.Add(Community.Find(4));
blog.Communities = list;
//保存
blog.Save();
接下来情况就变了
如再次执行相同代码则报错:第 1 行: '__' 附近有语法错误。
这时单独执行这两行:ArrayList list = new ArrayList(); list.Add(Community.Find(5));
则可以通过,如果再向社区6添加了Blog之后则以上代码执行不通过

我很是奇怪这个问题,为什么list.Add(Community.Find(5));这句会有问题呢?说白了Find()有问题
报错信息就是:System.Data.SqlClient.SqlException:第 1 行: '__' 附近有语法错误。
指到:return (Community) FindByPrimaryKey( typeof(Community), id);

希望TerryLee有时间能够解惑,在此多谢:P


  回复  引用  查看    
#7楼[楼主]2006-06-27 17:58 | TerryLee      
@一汐
不好意思,能不能把问题说明白一点

看着有些晕,真的:-)

  回复  引用  查看    
#8楼2006-06-28 10:01 | 一汐      
@TerryLee
呵呵,不好意思,也许我表达的有点罗嗦
其实就是不能把如下代码执行一边以上:
Blog blog = new Blog();
blog.Name = "Tech Space";
blog.Author = "Terrylee";
//属于ID为3,4社区
ArrayList list = new ArrayList();
list.Add(Community.Find(3));
list.Add(Community.Find(4));
blog.Communities = list;
//保存
blog.Save();
为什么执行第一次可以,第二次执行就不行了呢?
就是说我想添加另外一个blog到Community(3)或是Community(4)
似乎违背了多对多的原则
这时就报错了:
System.Data.SqlClient.SqlException: 第 1 行: '__' 附近有语法错误。
如果方便的话,你试试看

  回复  引用  查看    
#9楼2006-08-09 02:27 | jailu      
我加入HasAndBelongsMany属性后,那个Find()的方法一用就出错,报的是SQL语句的错误,是不是我的AR版本不对?还是配置出问题了?
  回复  引用  查看    
#10楼[楼主]2006-08-09 08:19 | TerryLee      
@jailu
你下载的是哪一个版本?应该是不是版本的问题吧,你看看ColumnRef,ColumnKey等对不对?

  回复  引用  查看    
#11楼2006-08-09 09:47 | jailu      
@TerryLee

我是Copy您的代码的,包括数据库脚本和实体类。
我的测试环境是:VS2005+Sql Server 2005+Castle2.0

  回复  引用  查看    
#12楼[楼主]2006-08-09 10:11 | TerryLee      
@jailu
这些都是1.1版本的,没有在2.0下做过测试,2.0可能有些不同!

  回复  引用  查看    
#13楼2006-08-09 11:00 | jailu      
@TerryLee

谢谢!我再试一下~
再问一下,ActiveRecord如何处理one-one关联关系和继承关系?

jailusd@hotmail.com

  回复  引用  查看    
#14楼2006-08-09 11:21 | jailu      
@TerryLee
我试过了,调用Castle1.1的也是一样出错啊

  回复  引用  查看    
#15楼2006-08-09 16:55 | jailu      
解决了,在Blog类的代码中
[HasAndBelongsToMany(typeof(Community),

Table="Blog_Community",

ColumnRef=" community_id ",

ColumnKey=" blog_id " )]

public IList Communities

{

get { return _community; }

set { _community = value; }

}
ColumnRef和ColumnKey属性性前后的空格去了就可以了,跟版本没关系

  回复  引用  查看    
#16楼[楼主]2006-08-09 16:59 | TerryLee      
@jailu
-_-

  回复  引用    
#17楼2006-08-13 02:57 | ditto[未注册用户]
如果数据库设计成以下这个样子,实体类应该怎么写呢?
CREATE TABLE Blogs (

id int IDENTITY(1, 1) PRIMARY KEY,

blog_name varchar(50),

blog_author varchar(50)

)



CREATE TABLE Blog_Community (

blog_Id int NOT NULL ,

community_Id int NOT NULL

)



CREATE TABLE Communities (

Id int IDENTITY (1, 1) PRIMARY KEY,

community_Name varchar (50) ,

community_Intro varchar (500)

)

  回复  引用  查看    
#18楼2006-08-13 18:57 | 无痕      
TerryLee兄
向您请教一个问题,上面的三个表中Blog_Community事实上是一个关系表。
当我不想删除Blog表和Community表的数据 只想删除掉Blog_Community上面的关系是最好怎样操作?

顺便指正一下,该文章的第三大节的第二小节的级连更新写少了两句话。

  回复  引用  查看    
#19楼[楼主]2006-08-14 08:40 | TerryLee      
@无痕
可以通过指定Cascade来实现的

你说得少了两句话是指?

  回复  引用  查看    
#20楼[楼主]2006-08-14 08:43 | TerryLee      
@ditto
=================================
[PrimaryKey(PrimaryKeyType.Identity, "id")]

public int Id

{

get { return _id; }

set { _id = value; }

}

注意一下PrimaryKey特性就可以了

  回复  引用  查看    
#21楼2006-08-14 09:59 | 无痕      
@TerryLee
呵呵!不好意思.是我刚才看错了。没写错。
你的Castle系列文章真的很不错。现在基本上没看见针对Castle专门教材,所以要经常上你这里来取经了。

  回复  引用  查看    
#22楼2006-08-14 10:19 | 无痕      
@TerryLee
Cascade是放在主表上面的。Blog表和Community表是主表,但是现在要删除的是Blog_Community表里面的数据但是保留Blog表和Community表的数据。在ActiveRecord里面是没有Blog_Community这个关系类的,怎样能指定Cascade呢? 例如Blog的1和3 都对应 Community的2和4。现在让BlogID的1只对应Community的4。只是删除掉和Community的2的关系,但是保留Blog的1和Community的2。

  回复  引用    
#23楼2006-09-07 18:03 | hongyanlai[未注册用户]
实现两个多对多的关系时,会出错,比如blog属于多个社区,而多个社区又属于多个网站,如何操作?
  回复  引用    
#24楼2006-11-13 09:30 | paleyyang[未注册用户]
[HasMany(typeof(Post), Table="posts", ColumnKey="post_blogid")]
这是一对多的配置呀.可以看明白.
但多对多就不明白了呢.
[HasAndBelongsToMany(typeof(Blog),
Table="Blog_Community",
ColumnRef="blog_id",
ColumnKey="community_id" )]
typeof(Blog)这里为Blog类,但Table就为Blog_Community了呢.还有ColumnRef和ColumnKey能讲讲么?

  回复  引用  查看    
#25楼[楼主]2006-11-13 11:37 | TerryLee      
@paleyyang
多对多的关系,中间不得有一张关联表嘛

ColumnRef和ColumnKey就分别是这两张表对应的在该表中的外键

同时在Blog和Community两个实体中都会出现HasAndBelongsToMany特性,可以对照这两个实体类看看,具体的在文章中都有说到

  回复  引用    
#26楼2006-11-13 21:19 | paleyyang[未注册用户]
嗯.谢谢.明白了一点.
  回复  引用  查看    
#27楼[楼主]2006-11-14 08:45 | TerryLee      
@paleyyang
:)

  回复  引用    
#28楼2007-01-18 10:44 | terryzhang[未注册用户]
有三个表order,product,order_product.在order_product表里除了orderid,productid还有discount字段。这种情况怎么处理discount字段呢?

能讲讲吗?

  回复  引用    
#29楼2007-08-15 22:05 | Prince[未注册用户]
@terryzhang
我也在考虑这问题
请TerryLee 说说

  回复  引用  查看    
#30楼2007-12-23 00:12 | Gavin.W.Lai(赖文华)      
初学中!非常感谢!
  回复  引用  查看    
#31楼2009-01-28 18:04 | BlackPhoenix      
一个问题搞了我一天,刚检查出来问题,连接字符串写错了!狂晕



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 370950




相关文章:

相关链接: