• Castle ActiveRecord学习实践(4):实现OneMany关系的映射


    摘要:前面几篇文章简单的介绍了ActiveRecord中的基本映射以及构建配置信息,本文我们用ActiveRecord里面的BlogPost例子来实现One-Many/Many-One关联。

    主要内容

    1.准备数据表结构

    2.编写实体类并介绍HasManyBlongsTo特性

    3.构建配置信息

    4.编写测试代码

     

    一.准备数据表结构

    在这个例子中,我们引入了两个对象BlogPost,它们之间的关系是一对多,即一个Blog有多篇Post。需要用到的数据表结构如下

    CREATE TABLE Blogs (

        blog_id     
    int IDENTITY(11PRIMARY KEY,

        blog_name   
    varchar(50),

        blog_author 
    varchar(50)

    )

     

    CREATE TABLE Posts (

        post_id        
    int IDENTITY(11PRIMARY 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关联映射。

     

    参考资料

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

  • 相关阅读:
    Eclipse 的控制台console乱码
    Cucumber java + Webdriver(一)
    安装 pywin32-218.win32-py2.7.exe 报错python version 2.7 required,which was not found in the registry解决方案
    安装pycharm软件后,打开robot framework怎么默认用pycharm打开
    C++中的智能指针
    RBF(径向基)神经网络
    C/C++指针参数赋值问题
    二叉树以及常见面试题
    对于正则化的理解
    GBDT算法
  • 原文地址:https://www.cnblogs.com/Terrylee/p/368471.html
Copyright © 2020-2023  润新知