1、继承映射(采用一实体一表,不冗余父表字段方式映射);
2、两种Tree结构映射方式(方式一:单表行冗余,采用用户事务维护tree关系;方式二:单表无冗余,Inner Join + Union);
3、演示Ilungasoft Framework中的一个新的用于简化处理关联实体的类OneToMany的使用。
下载
为了更好的理解示例建议先下载Sample4示例源码
基本数据表
数据表非常简单,一共六个,分别为:
1、Permission(ID,Title,ParentID)- 权限基本信息
2、User(ID,Name,Age)- 用户基本信息
3、Group(ID,Title,ParentID)- 组织基本信息
4、UserGroup(UserID,GroupID)- 用户、组织关联,该表采用单表行冗余(例如:如果添加用户到一个二层组织的纪录,则必须由用户在一个事务中添加用户到父组织的纪录的冗余行,删除是同理)方式存放数据
5、PermissionOfUser(PermissionID,UserID,Desc)- 赋给用户的权限子类,该表采用不冗余父表字段方式映射继承关系,同时采用单表无冗余(例如:如果将一个二层权限赋给用户,不需要用户添加父权限到用户的冗余纪录)方式存放数据
6、PermissionOfGroup(PermissionID,GroupID,Desc)- 赋给组织的权限子类,该表采用不冗余父表字段方式映射继承关系,同时采用单表无冗余方式存放数据
视图
本文的重点之一就在这里的视图了。
1、表UserGroup因为采用单表行冗余方式,也就意味着,正反查询直接查询该表就能获得完整的结果,只需要为了方便正反查询的同时获得User或Group的基本信息而建两个简单视图就行,这两个视图分别为:vUserInGroup和vGroupOfUser。
2、表PermissionOfUser因为采用单表无冗余方式,必须首先建立一个等价于单表行冗余的逻辑视图,视图vPermissionOfUserUnion2就是这样一个逻辑视图。他的SQL代码如下:
(select UserID, PermissionID from PermissionOfUser)
UNION (select PU.UserID, P.ID from PermissionOfUser as PU inner join Permission as P on PU.PermissionID = P.ParentID);
因为这里只演示两层的权限,如果可以有三层,则根据这个规则可以方便构造vPermissionOfUserUnion3,以此类推。
3、表PermissionOfGroup和视图vPermissionOfGroupUnion2的概念同上。
4、视图vPermissionOfGroup简单的在vPermissionOfGroupUnion2的基础上,同时获取Permission的基本信息,可以用于通过GroupID查询所有它所拥有的任意层级的权限,反向查询的的视图同理可建。
5、最后说说一类必须的查询——查询用户真正具有的权限(直接赋给用户的权限Union上用户通过所属组织被赋的权限而继承具有的权限),视图vPermissionOfUserUnionAll首先构造了这张逻辑关联表:
(SELECT UserGroup.UserID, vPermissionOfGroupUnion2.PermissionID
FROM UserGroup INNER JOIN vPermissionOfGroupUnion2 ON UserGroup.GroupID = vPermissionOfGroupUnion2.GroupID)
UNION (select UserID, PermissionID from vPermissionOfUserUnion2);
而视图vPermissionOfUser简单的为vPermissionOfUserUnionAll扩充Permission的基本信息。
测试
好了,构建表和视图的工作至此结束,下面可以来测试一下了。
1 private void btnLoadGroupsOfUser_Click(object sender, EventArgs e)
2 {
3 textBox1.Text = string.Empty;
4 OneToMany<User, User, UserGroup, vGroupOfUser> o2m = new OneToMany<User, User, UserGroup, vGroupOfUser>("UserID");
5 o2m.LoadOne("[ID] = @ID", 1);
6 textBox1.Text += SerializeHelper.Serialize(o2m.EntitySelectTypeOne) + "\r\n----\r\n";
7 textBox1.Text += SerializeHelper.SerializeArray(o2m.SelectMany(null, null)); //select all many entities
8 }
9
10 private void btnAddUsersToGroup_Click(object sender, EventArgs e)
11 {
12 textBox1.Text = string.Empty;
13 OneToMany<Group, Group, UserGroup, vUserInGroup> o2m = new OneToMany<Group, Group, UserGroup, vUserInGroup>("GroupID");
14 o2m.LoadOne("[ID] = @ID", 2);
15
16 //add user with ID 2 to loaded Group
17 vUserInGroup[] users = o2m.SelectMany("[ID] = @ID", null, 2); //no need to set the "[GroupID] = XX" where condition, because o2m auto appended this condition
18 if (users.Length == 0)
19 {
20 UserGroup newUserGroup = o2m.CreateNewEntityMany();
21 //no need to explictly set newUserGroup.GroupID, because CreateNewEntityMany() auto did this
22 newUserGroup.UserID = 2;
23 o2m.InsertMany(newUserGroup);
24 }
25
26 textBox1.Text += SerializeHelper.Serialize(o2m.EntitySelectTypeOne) + "\r\n----\r\n";
27 textBox1.Text += SerializeHelper.SerializeArray(o2m.SelectMany(null, null)); //select all many entities
28 }
29
30 private void btnLoadPermissionsOfUser_Click(object sender, EventArgs e)
31 {
32 textBox1.Text = string.Empty;
33 OneToMany<User, User, PermissionOfUser, vPermissionOfUser> o2m = new OneToMany<User, User, PermissionOfUser, vPermissionOfUser>("UserID");
34 o2m.LoadOne("[ID] = @ID", 1);
35 textBox1.Text += SerializeHelper.Serialize(o2m.EntitySelectTypeOne) + "\r\n----\r\n";
36 textBox1.Text += SerializeHelper.SerializeArray(o2m.SelectMany(null, null)); //select all many entities
37 }
38
39 private void btnTestAll_Click(object sender, EventArgs e)
40 {
41 textBox1.Text = string.Empty;
42
43 //add a new user
44 User newUser = DefaultGateway.Create<User>();
45 newUser.Name = "New User";
46 newUser.Age = 18;
47 DefaultGateway.Insert<User>(newUser, "ID");
48
49 //create a one to many object and add to group
50 OneToMany<User, User, UserGroup, vGroupOfUser> o2m = new OneToMany<User, User, UserGroup, vGroupOfUser>("UserID");
51 o2m.LoadOne("[Name] = @Name", "New User");
52 UserGroup newUserGroup = o2m.CreateNewEntityMany();
53 newUserGroup.GroupID = 1;
54 o2m.InsertMany(newUserGroup);
55
56 //create a one to many objetc and add some permissions to user
57 OneToMany<User, User, PermissionOfUser, vPermissionOfUser> o2m2 = new OneToMany<User, User, PermissionOfUser, vPermissionOfUser>("UserID");
58 o2m2.LoadOne("[ID] = @ID", o2m.EntityCreateTypeOne.ID);
59 PermissionOfUser newPou = o2m2.CreateNewEntityMany();
60 newPou.PermissionID = 8;
61 o2m2.InsertMany(newPou);
62 newPou.PermissionID = 10;
63 o2m2.InsertMany(newPou);
64
65 //display the user and permissions
66 textBox1.Text += SerializeHelper.Serialize(o2m2.EntitySelectTypeOne) + "\r\n----\r\n";
67 textBox1.Text += SerializeHelper.SerializeArray(o2m2.SelectMany(null, null)); //select all many entities
68
69 //delete the added user and all the groups and permissions
70 DbTransaction tran = DefaultGateway.BeginTransaction();
71
72 try
73 {
74 o2m.DeleteMany(null, tran);
75 o2m2.DeleteOne(true, tran);
76
77 tran.Commit();
78 }
79 catch
80 {
81 tran.Rollback();
82 throw;
83 }
84 finally
85 {
86 DefaultGateway.CloseTransaction(tran);
87 }
88 }
2 {
3 textBox1.Text = string.Empty;
4 OneToMany<User, User, UserGroup, vGroupOfUser> o2m = new OneToMany<User, User, UserGroup, vGroupOfUser>("UserID");
5 o2m.LoadOne("[ID] = @ID", 1);
6 textBox1.Text += SerializeHelper.Serialize(o2m.EntitySelectTypeOne) + "\r\n----\r\n";
7 textBox1.Text += SerializeHelper.SerializeArray(o2m.SelectMany(null, null)); //select all many entities
8 }
9
10 private void btnAddUsersToGroup_Click(object sender, EventArgs e)
11 {
12 textBox1.Text = string.Empty;
13 OneToMany<Group, Group, UserGroup, vUserInGroup> o2m = new OneToMany<Group, Group, UserGroup, vUserInGroup>("GroupID");
14 o2m.LoadOne("[ID] = @ID", 2);
15
16 //add user with ID 2 to loaded Group
17 vUserInGroup[] users = o2m.SelectMany("[ID] = @ID", null, 2); //no need to set the "[GroupID] = XX" where condition, because o2m auto appended this condition
18 if (users.Length == 0)
19 {
20 UserGroup newUserGroup = o2m.CreateNewEntityMany();
21 //no need to explictly set newUserGroup.GroupID, because CreateNewEntityMany() auto did this
22 newUserGroup.UserID = 2;
23 o2m.InsertMany(newUserGroup);
24 }
25
26 textBox1.Text += SerializeHelper.Serialize(o2m.EntitySelectTypeOne) + "\r\n----\r\n";
27 textBox1.Text += SerializeHelper.SerializeArray(o2m.SelectMany(null, null)); //select all many entities
28 }
29
30 private void btnLoadPermissionsOfUser_Click(object sender, EventArgs e)
31 {
32 textBox1.Text = string.Empty;
33 OneToMany<User, User, PermissionOfUser, vPermissionOfUser> o2m = new OneToMany<User, User, PermissionOfUser, vPermissionOfUser>("UserID");
34 o2m.LoadOne("[ID] = @ID", 1);
35 textBox1.Text += SerializeHelper.Serialize(o2m.EntitySelectTypeOne) + "\r\n----\r\n";
36 textBox1.Text += SerializeHelper.SerializeArray(o2m.SelectMany(null, null)); //select all many entities
37 }
38
39 private void btnTestAll_Click(object sender, EventArgs e)
40 {
41 textBox1.Text = string.Empty;
42
43 //add a new user
44 User newUser = DefaultGateway.Create<User>();
45 newUser.Name = "New User";
46 newUser.Age = 18;
47 DefaultGateway.Insert<User>(newUser, "ID");
48
49 //create a one to many object and add to group
50 OneToMany<User, User, UserGroup, vGroupOfUser> o2m = new OneToMany<User, User, UserGroup, vGroupOfUser>("UserID");
51 o2m.LoadOne("[Name] = @Name", "New User");
52 UserGroup newUserGroup = o2m.CreateNewEntityMany();
53 newUserGroup.GroupID = 1;
54 o2m.InsertMany(newUserGroup);
55
56 //create a one to many objetc and add some permissions to user
57 OneToMany<User, User, PermissionOfUser, vPermissionOfUser> o2m2 = new OneToMany<User, User, PermissionOfUser, vPermissionOfUser>("UserID");
58 o2m2.LoadOne("[ID] = @ID", o2m.EntityCreateTypeOne.ID);
59 PermissionOfUser newPou = o2m2.CreateNewEntityMany();
60 newPou.PermissionID = 8;
61 o2m2.InsertMany(newPou);
62 newPou.PermissionID = 10;
63 o2m2.InsertMany(newPou);
64
65 //display the user and permissions
66 textBox1.Text += SerializeHelper.Serialize(o2m2.EntitySelectTypeOne) + "\r\n----\r\n";
67 textBox1.Text += SerializeHelper.SerializeArray(o2m2.SelectMany(null, null)); //select all many entities
68
69 //delete the added user and all the groups and permissions
70 DbTransaction tran = DefaultGateway.BeginTransaction();
71
72 try
73 {
74 o2m.DeleteMany(null, tran);
75 o2m2.DeleteOne(true, tran);
76
77 tran.Commit();
78 }
79 catch
80 {
81 tran.Rollback();
82 throw;
83 }
84 finally
85 {
86 DefaultGateway.CloseTransaction(tran);
87 }
88 }
Sample4是一个WinForm程序(千万别以为Ilungasoft Framework只能用于Web开发,实际上除了Ilungaosft.Framework.Web命名空间,其他功能对WinForm程序是一样适用的,使用也是一样简单),大家可以自行运行,这里仅仅列举一下主要代码。
简单说明一下OneToMany的功能,OneToMany是一个泛型类,它接受四个泛型参数:
public class OneToMany<CreateTypeOfOne, SelectTypeOfOne, CreateTypeOfMany, SelectTypeOfMany>
where CreateTypeOfOne : IEntity
where SelectTypeOfOne : IEntity
where CreateTypeOfMany : IEntity
where SelectTypeOfMany : IEntity;
前两个参数表示One的类型,后两个表示Many的类型,CreateType表示insert/update是需要使用的实际类型,SelectType则可以是对应于Table的Entity也可以是对应于View的Entity。
现实数据库中的所有One to Many和Many to Many关系都可以由这个类来表示和简化操作。使用时,构造函数需要传入一个Many表中的外键的字段名称,并且必须先通过LoadOne函数载入One实例,否则其他函数都将不可用。
好了,本次的实例介绍就到这里,Enjoy!