摘要:多对多的关系在日常开发中也会经常遇到,在ActiveRecord中我们用HasAndBelongsToMany特性来实现Many-Many的关联,本文将通过一个具体的实例来介绍这一用法。
主要内容
1.准备数据库表
2.编写实体类
3.编写测试代码
一.准备数据库表
接着在上篇文章中的例子,为了实现多对多的关系,我们引入Community,即每个Blog可以属于多个社区,每个社区也可以有多个Blog。
CREATE TABLE Blogs (

blog_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 (

community_Id int IDENTITY (1, 1) PRIMARY KEY,

community_Name varchar (50) ,

community_Intro varchar (500)

)
二.编写实体类代码
为了实现多对多的关系,我们要在Blog、Community类中分别使用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
|
指定一个附加SQL的Where子句
|
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
posted @ 2006-04-10 08:17
TerryLee 阅读(7496)
评论(31) 编辑 收藏 网摘 所属分类:
[11] 数据持久[12] 开源世界
发表评论
我记得nh中m2m有一头好像要inverse, 否则取不到值(也可能是我编程有问题:))
不知在castle里是这么解决这个问题的
#3楼[
楼主]2006-04-10 12:52 |
@windwolf
Castle ActiveRecord里面也支持inverse,它只不过是对于NH做了一层封装,可能会对inverse赋了初始值!
@Terrylee
我加入HasAndBelongsMany属性后,那个Find()的方法一用就出错,报的是SQL语句的错误,是不是我的AR版本不对?还是配置出问题了?
看过的所有Where语句相关的示例都以HasMany说明,请教在HasAndBelongsToMany中使用Where语句,主要是使用什么前缀。
@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
呵呵,不好意思,也许我表达的有点罗嗦
其实就是不能把如下代码执行一边以上:
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 行: '__' 附近有语法错误。
如果方便的话,你试试看
我加入HasAndBelongsMany属性后,那个Find()的方法一用就出错,报的是SQL语句的错误,是不是我的AR版本不对?还是配置出问题了?
#10楼[
楼主]2006-08-09 08:19 |
@jailu
你下载的是哪一个版本?应该是不是版本的问题吧,你看看ColumnRef,ColumnKey等对不对?
@TerryLee
我是Copy您的代码的,包括数据库脚本和实体类。
我的测试环境是:VS2005+Sql Server 2005+Castle2.0
#12楼[
楼主]2006-08-09 10:11 |
@jailu
这些都是1.1版本的,没有在2.0下做过测试,2.0可能有些不同!
@TerryLee
谢谢!我再试一下~
再问一下,ActiveRecord如何处理one-one关联关系和继承关系?
jailusd@hotmail.com
@TerryLee
我试过了,调用Castle1.1的也是一样出错啊
解决了,在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 |
@jailu
-_-
如果数据库设计成以下这个样子,实体类应该怎么写呢?
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)
)
TerryLee兄
向您请教一个问题,上面的三个表中Blog_Community事实上是一个关系表。
当我不想删除Blog表和Community表的数据 只想删除掉Blog_Community上面的关系是最好怎样操作?
顺便指正一下,该文章的第三大节的第二小节的级连更新写少了两句话。
#19楼[
楼主]2006-08-14 08:40 |
@无痕
可以通过指定Cascade来实现的
你说得少了两句话是指?
#20楼[
楼主]2006-08-14 08:43 |
@ditto
=================================
[PrimaryKey(PrimaryKeyType.Identity, "id")]
public int Id
{
get { return _id; }
set { _id = value; }
}
注意一下PrimaryKey特性就可以了
@TerryLee
呵呵!不好意思.是我刚才看错了。没写错。
你的Castle系列文章真的很不错。现在基本上没看见针对Castle专门教材,所以要经常上你这里来取经了。
@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。
实现两个多对多的关系时,会出错,比如blog属于多个社区,而多个社区又属于多个网站,如何操作?
[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 |
@paleyyang
多对多的关系,中间不得有一张关联表嘛
ColumnRef和ColumnKey就分别是这两张表对应的在该表中的外键
同时在Blog和Community两个实体中都会出现HasAndBelongsToMany特性,可以对照这两个实体类看看,具体的在文章中都有说到
#27楼[
楼主]2006-11-14 08:45 |
@paleyyang
:)
有三个表order,product,order_product.在order_product表里除了orderid,productid还有discount字段。这种情况怎么处理discount字段呢?
能讲讲吗?
@terryzhang
我也在考虑这问题
请TerryLee 说说
一个问题搞了我一天,刚检查出来问题,连接字符串写错了!狂晕