zoukankan      html  css  js  c++  java
  • NBearV3——ORM实体关系设计速查手册

    版本

    1.2 [2006-11-13]

    简介

    本手册演示NBearV3支持的所有实体关系设计的完全参考。包括111对多,多对多关联以及自关联的正向、反向引用时的所有情况的设计方法。

    1:本手册并不讨论继承关系。因为,继承关系自然映射到设计实体接口的继承,无需太多额外讨论。

    2:所有的关联在演示中都包含了正向和反向的可读写引用,在实际的项目中,一般并不总是需要同时有正向和反向引用,可以只在一个方向包含引用,或一方包含引用,另一方只包含一个外键ID,也可以只包含只读的引用。并且,在双向引用时,绝对不能同时设置为双向的LazyLoad=false

    3:所有关联关系中的正向或反向引用的属性,根据需要,可以添加ContainedAttribute标识,以实现属性和包含属性的实体的级联更新/删除。在本文演示的所有引用中都没有包含ContainedAttribute,实际项目中请注意添加,但是不要同时在正向和反向引用属性中添加ContainedAttribute

    4:在阅读本文之前,建议读者先阅读《NBearV3 Step by Step教程——ORM》以掌握NBearV3中有关ORM的基本知识。

    代码

    本手册演示的所有类图和代码,包含于可以从sf.net下载的NBearV3最新源码zip包中的tutorials\ Entity_Relation_Manual目录中。因此,在使用本手册的过程中如有任何疑问,可以直接参考这些代码。

    正文

    一、11主键关联



    分析:一对一主键关联指的是两个实体通过相同的主键进行关联。典型的关联关系如图中的
    UserUserProfile的关联。其中,UserProfile不能脱离具有相同主键值的User存在。因此,实际上,对于UserProfile来说,它的属性UserID既是它的PK,又是一个关联到UserFK,且UserProfile.UserID应该对User.ID有引用完整性约束。

        public interface User : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [PkQuery(LazyLoad
    =true)]
            UserProfile Profile
            
    {
                
    get;
                
    set;
            }

        }


        
    public interface UserProfile : Entity
        
    {
            [PrimaryKey]
            [FriendKey(
    typeof(User))]
            Guid UserID 
    getset; }
            
    string Content getset; }

            [PkReverseQuery(LazyLoad 
    = true)]
            User User
            
    {
                
    get;
                
    set;
            }

        }

    二、11外键关联



    分析:一对一外键关联指的是一个实体通过自己的一个外键和另一个实体进行关联。一对一外键关联无论在关联语义还是数据库映射上都和
    1对多(外键)关联非常类似,所以,对于1对多(外键)关联中FkQueryFkReverseQuery的解释同样适用于11外键关联的情形。典型的关联关系如图中的UserUserProfile的关联。这里和前面的11主键关联的不同在于:此时,UserProfile.IDUser.ID并不相关,分别只作为各自实体的主键,UserProfile.UserID是一个外键,UserProfile通过这个外键关联到User实体。UserProfile.UserID应该对User.ID有引用完整性约束。

        public interface User : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [FkQuery("User", LazyLoad
    =true)]
            UserProfile Profile
            
    {
                
    get;
                
    set;
            }

        }


        
    public interface UserProfile : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Content getset; }

            [FkReverseQuery(
    LazyLoad=true)]
            User User
            
    {
                
    get;
                
    set;
            }

        }


    注:标识在
    UserProfile属性上的FkQuery的构造函数需要一个输入参数,该参数指定该外键查询引用属性对应的反向引用属性是UserProfile实体的User属性。

    三、11自关联

    分析:11自关联类似11外键关联,区别仅仅在于,关联的双方是同一个实体。

        public interface Person : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [FkReverseQuery(LazyLoad
    =true)]
            [MappingName(
    "MateID")]
            [SerializationIgnore]
            Person Mate 
    getset; }
        }

    1:一对一自关联可以只使用一个属性表示双向的引用,也可以使用正反两个属性。但是,必须注意至少设置其中一个引用为LazyLoad=true,且至少设置其中一个引用为SerializationIgnore,以避免序列化时的死循环。

    2:注意Person.Mate中的MappingName(“MateID”)。该Attribute是可选的。这里显式指定为MateID,表示Person实体映射到数据库表时,需要创建的用于关联Mate属性的字段名称为MateID。如果不指定,则默认值一般为属性名_主键名,以这里为例,如果不指定这个MappingName,则这个字段的名称为Mate_ID,因为属性名为Mate,关联类型Person的主键字段名为ID。本手册中其他为关联属性指定的MappingName的含义都同该注解。 

    四、1对多关联

    分析:一对多关联指一个实体通过被关联实体的一个外键关联多个被关联实体的集合。举例来说,下面的图表示Team可以包含多个User的关联。

        public interface Team : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [FkQuery(
    "Team", OrderBy="{Name} DESC", Contained=true, LazyLoad=true)]
            User[] Users 
    getset; }
        }


        
    public interface User : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [FkReverseQuery(LazyLoad
    =true)]
            Team Team 
    getset;}
        }

    1FkQueryOrderBy属性,可以指定一个类似SQL语法的排序规则,用于对一组关联的对象进行排序。本手册中的其他OrderBy属性的含义总是和这里的含义相同。

    2FkQuery的构造函数的第一个参数(如Team.Users属性中OneToManyQuery构造函数的第一个参数Team)指定对应的被关联实体中的关联属性。这个关联属性可以是一个FkReverseQuery属性(即双向引用),也可以是一个外键属性(即只在One实体包含到Many实体的引用)。本例中演示的是双向引用,如果只想包含单向引用的话,可以将User.Team属性改为一个简单的外键属性,并将OneToManyQuery构造函数的第一个参数Team修改为这个外键属性的名称,示例代码如下:

        public interface Team : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [FkQuery(
    "TeamID", Contained=true, LazyLoad=true)]
            User[] Users 
    getset; }
        }


        
    public interface User : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [FriendKey(
    typeof(Team))]
            Guid TeamID 
    getset; }
        }

    3:FkQuery允许设置一个Contained属性,使得被关联对象元素随主对象更新时自动级联更新。本手册中其他AttributeContained属性的含义,总是和这里的Contained的含义相同。


    五、
    1对多自关联

    分析:1对多自关联和普通的1对多关联的定义方法完全相同。

        public interface Group : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [FkReverseQuery(LazyLoad
    =true)]
            [MappingName(
    "ParentID")]
            Group ParentGroup 
    getset; }

            [FkQuery(
    "ParentGroup", LazyLoad=true)]
            [SerializationIgnore]
            Group[] ChildGroups 
    getset; }
        }

    注:一对多自关联时,同样必须注意至少设置其中一个引用为LazyLoad=true,且至少设置其中一个引用为SerializationIgnore,以避免序列化时的死循环。 

    六、多对多关联

    分析:多对多关联指一个实体可以关联另一个实体的多个对象,而这个被关联实体,也可以关联到这个实体的多个对象。以下面的GroupUser的关联为例,Group中可以有多个UserUser也可以在多个Group中。在映射多对多关联的的实体到数据库表时,我们总是需要有一个独立的关联表,对应这里的UserGroup关联实体。

        public interface Group : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [ManyToManyQuery(
    typeof(UserGroup), LazyLoad=true)]
            User[] Users 
    getset; }
        }


        
    public interface User : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [ManyToManyQuery(
    typeof(UserGroup), LazyLoad = true)]
            Group[] Groups 
    getset;}
        }


        [Relation]
        
    public interface UserGroup : Entity
        
    {
            [RelationKey(
    typeof(User))]
            Guid UserID 
    getset; }

            [RelationKey(
    typeof(Group))]
            Guid GroupID 
    getset; }
        }

    1:用于多对多关联的关联实体必须使用RelationAttribute标识。它的用于关联多和多双方的属性,必须用RelationKeyAttribute标识,RelationKeyAttribute的唯一一个构造函数参数,如这个里的typeof(User)typeof(Group)分别表示,这两个属性用于关联到这两个实体。

    2:在实体GroupUser中,我们必须为属性UsersGroups设置ManyToManyQueryAttribute,并且用关联实体UserGroup的类型值作为ManyToManyQuery的唯一的构造函数参数。

    七、自定义关联

    分析:除了以上常规的关联关系之外,NBear还支持使用CustomQuery设置自定义查询关联属性。CustomQuery可以实现在普通的111对多和多对多关联查询的基础上附加额外的约束条件。它和上面的常规查询的不同在于,自定义查询关联的对象不会被级联更新。这些额外的约束条件一般也是一些简单条件,但是灵活运用还是可以极大的减少我们的业务类代码。

    下面的例子比较复杂,读者请留神看了。首先,这里主要包括三个实体TeamGroupUserTeamUser是一对多关联,UserGroup是多对多关联,这两个关系和我们上面演示的类似。但是,User实体还包含了一些附加的CustomQuery关联属性:



        public interface Team : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }
            
    bool IsAvailable getset; }

            [FkQuery(
    "Team", OrderBy = "{Name} DESC", Contained = true, LazyLoad = true)]
            User[] Users 
    getset; }
        }


        
    public interface User : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }

            [ManyToManyQuery(
    typeof(UserGroup), LazyLoad = true)]
            Group[] Groups 
    getset;}

            [FkReverseQuery(LazyLoad 
    = true)]
            Team Team 
    getset;}

            [CustomQuery(
    "{LeaderName} = @Name AND {IsPublic} = 1")]
            Group[] PublicLeadingGroups
            
    {
                
    get;
                
    set;
            }


            [CustomQuery(
    "{IsAvailable} = 1", OrderBy="{Name}")]
            Team AvailableTeams
            
    {
                
    get;
                
    set;
            }


            [CustomQuery(
    "{IsHidden} = 1", RelationType=typeof(UserGroup), OrderBy="{ID} DESC")]
            Group[] DivingGroups
            
    {
                
    get;
                
    set;
            }

        }


        
    public interface Group : Entity
        
    {
            [PrimaryKey]
            Guid ID 
    getset; }
            
    string Name getset; }
            
    string LeaderName getset; }
            
    bool IsPublic getset; }

            [ManyToManyQuery(
    typeof(UserGroup), LazyLoad = true)]
            User[] Users 
    getset; }

        }


        [Relation]
        
    public interface UserGroup : Entity
        
    {
            [RelationKey(
    typeof(User))]
            Guid UserID 
    getset; }

            [RelationKey(
    typeof(Group))]
            Guid GroupID 
    getset; }

            
    bool IsHidden getset;}
        }

    注意,其中User.PublicLeadingGroups表示公开的,LeaderNameUser.Name的所有GroupsUser.AvailableTeams代表系统中所有IsAvailableTeam;特别注意User.DivingGroups代表了User所在的,但是,又是潜水(意思是UserGroup中的状态对Group中的其他成员是不可见的)的Groups,这个DivingGroupsCustomQuery标注的特殊之处在于,它的查询约束条件”{IsHidden} = 1”中的IsHiddenUserGroup的关联实体UserGroup中的属性,所以,这个CustomQuery标注也包含了RelationType属性指定了关联实体是UserGroup。同时,我们也可以使用OrderBy属性,指定默认的排序规则。

    正文结束

    //本文结束

  • 相关阅读:
    书单
    x&(x1)表达式的意义
    约瑟夫环,杀人游戏(静态循环链表实现)
    我的第一个动态规划程序(试图用递归求斐波拉契数)
    NYOJ 2题 括号配对问题
    为什么 C++不叫作++C? o(∩_∩)o
    文字常量区,字符串常量
    括号匹配(栈实现)
    Mybatis的逆向工程(generator)
    Mybatis学习一(介绍/举例/优化)
  • 原文地址:https://www.cnblogs.com/teddyma/p/554823.html
Copyright © 2011-2022 走看看