Castle ActiveRecord学习实践(4):实现One-Many关系的映射

简介:
摘要:前面几篇文章简单的介绍了ActiveRecord中的基本映射以及构建配置信息,本文我们用ActiveRecord里面的BlogPost例子来实现One-Many/Many-One关联。
主要内容
1 .准备数据表结构
2 .编写实体类并介绍HasManyBlongsTo特性
3 .构建配置信息
4 .编写测试代码
 
一.准备数据表结构
在这个例子中,我们引入了两个对象BlogPost,它们之间的关系是一对多,即一个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
指定一个附加SQLWhere子句
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 选项有三个:AutoTrueFalse
最后完整的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,只不过对HasManyBlongsTo设置的位置不一样而已,在下一篇文章中我会介绍在ActiveRecord中实现Many-Many关联映射。













本文转自lihuijun51CTO博客,原文链接: http://blog.51cto.com/terrylee/67660  ,如需转载请自行联系原作者

相关文章
|
7月前
|
Java
JavaSE——面向对象高级一(3/4)-面向对象三大特征之二:继承(初步认识继承、了解继承的好处)
JavaSE——面向对象高级一(3/4)-面向对象三大特征之二:继承(初步认识继承、了解继承的好处)
31 0
|
SQL NoSQL PHP
Thinkphp中模型的正确使用方式,ORM的思想概念
对象-关系映射(OBJECT-RELATIONAL MAPPING,简称ORM) 这是在面向对象编程发展过程中演变出来的一种思想、行为概念。 主要用途是:把对象模型表示的对象映射到基于sql的关系模型数据库结构中去。 当改变这个对象自身的属性或者调用该对象的方法时,相对应的是执行某些sql语句。 这样子编写代码的人员就可以更好地编写业务逻辑,而非重复地编写增删改查sql语句。
274 0
|
SQL Java 数据库连接
Hibernate【映射】续篇(三)
Hibernate【映射】续篇
132 0
Hibernate【映射】续篇(三)
|
Java 数据库连接 数据库
Hibernate【映射】续篇(一)
Hibernate【映射】续篇
112 0
Hibernate【映射】续篇(一)
|
Java 数据库连接 数据库
Hibernate【映射】续篇(二)
Hibernate【映射】续篇
116 0
Hibernate【映射】续篇(二)
|
SQL Java 数据库连接
Hibernate【映射】知识要点(二)
Hibernate【映射】知识要点
152 0
Hibernate【映射】知识要点(二)
|
Java 数据库连接 数据库
Hibernate【映射】知识要点(一)
Hibernate【映射】知识要点
126 0
Hibernate【映射】知识要点(一)
|
Java 数据安全/隐私保护
复杂中的艺术-复杂多对多映射 | 带你学《Java面向对象编程》之二十五
在上一节中已经为读者介绍了简单映射关系的实现方式,但在现实世界中往往会涉及更多更复杂的映射关系。
|
XML Java 数据库连接
Hibernate【映射】知识要点
前言 前面的我们使用的是一个表的操作,但我们实际的开发中不可能只使用一个表的...因此,本博文主要讲解关联映射 集合映射 需求分析:当用户购买商品,用户可能有多个地址。 数据库表 我们一般如下图一样设计数据库表,一般我们不会在User表设计多个列来保存地址的。
1031 0