1. 感言
写博客之前先自我吹嘘一下,给这些文章来些自我介绍。
半年前自己借用了5个多月的业务时间写了一个个人ORM映射框架。在之前的博
客中也有过写过该框架的相关介绍。半年前的那个ORM只不过是自己想象的关系映射的一个雏形,那一段曾经让自己骄傲过得代码的确存在着太多的问题,但是我始终没有放弃过对它的修改。又过了半年,这个ORM映射框架在之前的基础上有了很大的改进。在此与大家分享一下,希望大家共同探讨,共同学习。
2. 框架整体介绍
说道这里,其实这个ORM框架仍然存在这很大的问题。不过这个是自己的第二期目标,到现在这个程度算是完成了.
目前出现的ORM 框架还是比较多的。本人写这个ORM不是为了说要推翻某个理论或者要和Linq,Nhibernate 一教高下,目前一个人之力几乎不可能。写此框架只是为了更近一步的了解程序架构。
设计思路其实很简单,和其余的ORM框架一样。通过某种关系来维持实体对象和数据库之间的关系,然后通过实体对象的操作来实现数据库的操作。
ORM是通过使用描述对象和数据库之间映射的元数据,在我们想到描述的时候自然就想到了xml和特性(Attribute).目前的ORM框架中,Nhibernate 就是典型的使用xml文件作为描述实体对象的映射框架,而大名鼎鼎的Linq 则是使用特性(Attribute) 来描述的。
我自己写的这个出于学习特性这一知识,于是采用了特性(Attribute)来描述实体对象。
ORM 映射功能的实现组要由如下几个组建组成:
(1) 实体—数据库 映射特性关系
(2) 实体分析器
(3) Sql 语句生成组建
(4) 数据库操作库
框架整体结构图:
3. 实体—数据库映射特性
对于数据库和实体之间关系的描述,这个框架中使用了四个特性来描述。
TableAttribute 特性
字段名 |
作用 |
name |
数据表名 该字段用于描述数据库对应表的名称,而且该值最好与 数据表名大小写相同。该值有两种类型。 (1)直接自定表的名称 (2)[数据库名].[表名] 如果是(2)情况,则需要分割字符串,将数据库名分割 出来赋值给dBName |
dBName |
数据库名 该字段用于描述数据的名称,而且该值最好与 数据库名称大小写相同 |
primaryKeyName |
主键字段名 该实体必须指定对应数据库表的主键 |
information |
表实体描述信息 默认为"" |
isInternal |
表实体是否国际化 默认为false true 国际化 false 不国际化 |
version |
表实体版本号 默认为 "V1.0" |
|
|
特性源码:
1 /**
3 *
4 * 情 缘
5 *
6 * 实体表特性,用于描述实体和数据库表之间的映射关系
7 *
8 * 该特性类使用 sealed 修改,说明该类不能够再被继承。
9 *
10 * */
11
12 using System;
13
14 namespace CommonData.Model.Core
15 {
16 public sealed class TableAttribute:Attribute
17 {
18 /// <summary>
19 /// 数据表名
20 /// 该字段用于描述数据库对应表的名称,而且该值最好与
21 /// 数据表名大小写相同。该值有两种类型。
22 /// (1)直接自定表的名称
23 /// (2)[数据库名].[表名]
24 /// 如果是(2)情况,则需要分割字符串,将数据库名分割
25 /// 出来赋值给dBName
26 /// </summary>
27 private string name;
28
29 /// <summary>
30 /// 数据库名
31 /// 该字段用于描述数据的名称,而且该值最好与
32 /// 数据库名称大小写相同
33 /// </summary>
34 private string dBName;
35
36 /// <summary>
37 /// 主键字段名
38 /// 该实体必须指定对应数据库表的主键
39 /// </summary>
40 private string primaryKeyName = "" ;
41
42 /// <summary>
43 /// 表实体描述信息
44 /// 默认为""
45 /// </summary>
46 private string information = "" ;
47
48 /// <summary>
49 /// 表实体是否国际化
50 /// 默认为false
51 /// true 国际化 false 不国际化
52 /// </summary>
53 private bool isInternal = false ;
54
55 /// <summary>
56 /// 表实体版本号
57 /// 默认为 "V1.0"
58 /// </summary>
59 private string version = " V1.0 " ;
60
61
62 /// <summary>
63 /// 无参数构造方法
64 /// </summary>
65 public TableAttribute()
66 {
67
68 }
69
70 /// <summary>
71 /// 部分参数构造方法,构造改表特性的实体类
72 /// 并实现部分字段的初始化
73 /// </summary>
74 /// <param name="name"> 数据表名 </param>
75 /// <param name="dBName"> 数据库名 </param>
76 /// <param name="primaryKeyName"> 主键字段名 </param>
77 /// <param name="isInternal"> 表实体是否国际化 </param>
78 public TableAttribute( string name,
79 string dBName,
80 string primaryKeyName,
81 bool isInternal)
82 {
83 this .name = name;
84 this .dBName = dBName;
85 this .primaryKeyName = primaryKeyName;
86 this .isInternal = isInternal;
87 }
88
89 /// <summary>
90 /// 全参数构造方法,构造改表特性的实体类
91 /// 并实现所有字段的初始化
92 /// </summary>
93 /// <param name="name"> 数据表名 </param>
94 /// <param name="dBName"> 数据库名 </param>
95 /// <param name="primaryKeyName"> 主键字段名 </param>
96 /// <param name="information"> 表实体描述信息 </param>
97 /// <param name="isInternal"> 表实体是否国际化 </param>
98 /// <param name="version"> 表实体版本号 </param>
99 public TableAttribute( string name,
100 string dBName,
101 string primaryKeyName,
102 string information,
103 bool isInternal,
104 string version)
105 {
106 this .name = name;
107 this .dBName = dBName;
108 this .primaryKeyName = primaryKeyName;
109 this .information = information;
110 this .isInternal = isInternal;
111 this .version = version;
112 }
113
114 /// <summary>
115 /// 数据表名
116 /// </summary>
117 public string Name
118 {
119 get { return name; }
120 set { name = value; }
121 }
122
123 /// <summary>
124 /// 数据库名
125 /// </summary>
126 public string DBName
127 {
128 get { return dBName; }
129 set { dBName = value; }
130 }
131
132 /// <summary>
133 /// 主键字段名
134 /// </summary>
135 public string PrimaryKeyName
136 {
137 get { return primaryKeyName; }
138 set { primaryKeyName = value; }
139 }
140
141 /// <summary>
142 /// 表实体描述信息
143 /// </summary>
144 public string Information
145 {
146 get { return information; }
147 set { information = value; }
148 }
149
150 /// <summary>
151 /// 表实体是否国际化
152 /// </summary>
153 public bool IsInternal
154 {
155 get { return isInternal; }
156 set { isInternal = value; }
157 }
158
159 /// <summary>
160 /// 表实体版本号
161 /// </summary>
162 public string Version
163 {
164 get { return version; }
165 set { version = value; }
166 }
167
168
169 }
170 }
171
特性说明:实体表特性,用于描述实体和数据库表之间的映射关系,该特性类使用 sealed 修改,说明该类不能够再被继承。
ColumnAttribute特性
字段名 |
作用 |
name |
表字段名称,该属性的值最好与数据表的字段的名称相同。 该字段的值有两种格式: (1) [表名].[字段名] (2) [字段名] 如果该字段的值为(1)情况,则应分割字符串,将字段名 赋值给name属性,表名则赋值给tableName |
tableName |
表字段对应表名称 该值是可以为空的,如果name的值的情况满足(1)情况, 可以分割的值赋值给该属性 |
dataType |
表字段的数据类型 该属性的类型为自定义类型,该字段是一个枚举类型。 该字段描述了25中数据类型 |
length |
表字段的长度 控制该字段对应的数据库表字段值的最大长度 可以不指定该值 |
canNull |
表字段是否可以为空 true 可以为空 false 不能为空 |
defaultValue |
表字段的默认值 默认情况为null |
isPrimaryKey |
表字段是否为主键 true 为主键 false 不是外键 |
autoIncrement |
表字段是否为自动增长列 true 是自动增长列 false 不是自动增长列 |
isUnique |
确定某个字段是否唯一 true 是唯一的 false 不是唯一 |
regularExpress |
表字段的匹配规则 字段匹配规则正则表达式 |
isForeignKey |
表字段是否为外键 true 为外键 false 不是外键 |
foreignTabName |
表字段外键对应的表名称 如果isForeignKey 为true,则需要指定其值 |
information |
表字段的描述信息 |
|
|
特性源码:
1 /**
3 *
4 * 情 缘
5 *
6 * 实体表特性,用于描述实体字段和数据库表字段之间的映射关系
7 *
8 * 该特性类使用 sealed 修改,说明该类不能够再被继承。
9 *
10 * */
11
12 using System;
13
14 namespace CommonData.Model.Core
15 {
16 public sealed class ColumnAttribute:Attribute
17 {
18 /// <summary>
19 /// 表字段名称,该属性的值最好与数据表的字段的名称相同。
20 /// 该字段的值有两种格式:
21 /// (1) [表名].[字段名]
22 /// (2) [字段名]
23 /// 如果该字段的值为(1)情况,则应分割字符串,将字段名
24 /// 赋值给name属性,表名则赋值给tableName
25 /// </summary>
26 private string name;
27
28 /// <summary>
29 /// 表字段对应表名称
30 /// 该值是可以为空的,如果name的值的情况满足(1)情况,
31 /// 可以分割的值赋值给该属性
32 /// </summary>
33 private string tableName;
34
35 /// <summary>
36 /// 表字段的数据类型
37 /// 该属性的类型为自定义类型,该字段是一个枚举类型。
38 /// 该字段描述了25中数据类型
39 /// </summary>
40 private DataType dataType;
41
42 /// <summary>
43 /// 表字段的长度
44 /// 控制该字段对应的数据库表字段值的最大长度
45 /// 可以不指定该值
46 /// </summary>
47 private int length;
48
49 /// <summary>
50 /// 表字段是否可以为空
51 /// true 可以为空
52 /// false 不能为空
53 /// </summary>
54 private bool canNull;
55
56 /// <summary>
57 /// 表字段的默认值
58 /// 默认情况为null
59 /// </summary>
60 private object defaultValue;
61
62 /// <summary>
63 /// 表字段是否为主键
64 /// true 为主键
65 /// false 不是外键
66 /// </summary>
67 private bool isPrimaryKey = false ;
68
69 /// <summary>
70 /// 表字段是否为自动增长列
71 /// true 是自动增长列
72 /// false 不是自动增长列
73 /// </summary>
74 private bool autoIncrement = false ;
75
76 /// <summary>
77 /// 确定某个字段是否唯一
78 /// true 是唯一的
79 /// false 不是唯一
80 /// </summary>
81 private bool isUnique;
82
83 /// <summary>
84 /// 表字段的匹配规则
85 /// 字段匹配规则正则表达式
86 /// </summary>
87 private string regularExpress;
88
89 /// <summary>
90 /// 表字段是否为外键
91 /// true 为外键
92 /// false 不是外键
93 /// </summary>
94 private bool isForeignKey = false ;
95
96 /// <summary>
97 /// 表字段外键对应的表名称
98 /// 如果isForeignKey 为true,则需要指定其值
99 /// </summary>
100 private string foreignTabName;
101
102 /// <summary>
103 /// 表字段的描述信息
104 /// </summary>
105 private string information;
106
107
108 /// <summary>
109 /// 无参数构造方法
110 /// </summary>
111 public ColumnAttribute()
112 {
113 }
114
115 /// <summary>
116 /// 部分参数构造方法,构造该特性实例时
117 /// 初始化部分实体属性的值
118 /// </summary>
119 /// <param name="name"> 表字段名称 </param>
120 /// <param name="dataType"> 表字段的数据类型 </param>
121 /// <param name="isPrimaryKey"> 表字段是否为主键 </param>
122 /// <param name="autoIncrement"> 表字段是否为自动增长列 </param>
123 /// <param name="isUnique"> 确定某个字段是否唯一 </param>
124 /// <param name="isForeignKey"> 表字段是否为外键 </param>
125 /// <param name="foreignTabName"> 表字段外键对应的表名称 </param>
126 public ColumnAttribute( string name,
127 DataType dataType,
128 bool isPrimaryKey,
129 bool autoIncrement,
130 bool isUnique,
131 bool isForeignKey,
132 string foreignTabName)
133 {
134 this .name = name;
135 this .dataType = dataType;
136 this .isPrimaryKey = isPrimaryKey;
137 this .autoIncrement = autoIncrement;
138 this .isUnique = isUnique;
139 this .isForeignKey = isForeignKey;
140 this .foreignTabName = foreignTabName;
141 }
142
143 /// <summary>
144 /// 全部参数构造方法,构造该特性实例时
145 /// 初始化全部实体属性的值
146 /// </summary>
147 /// <param name="name"> 表字段名称 </param>
148 /// <param name="tableName"> 表字段对应表名称 </param>
149 /// <param name="dataType"> 表字段的数据类型 </param>
150 /// <param name="length"> 表字段的长度 </param>
151 /// <param name="canNull"> 表字段是否可以为空 </param>
152 /// <param name="defaultValue"> 表字段的默认值 </param>
153 /// <param name="isPrimaryKey"> 表字段是否为主键 </param>
154 /// <param name="autoIncrement"> 表字段是否为自动增长列 </param>
155 /// <param name="isUnique"> 确定某个字段是否唯一 </param>
156 /// <param name="regularExpress"> 表字段的匹配规则 </param>
157 /// <param name="isForeignKey"> 表字段是否为外键 </param>
158 /// <param name="foreignTabName"> 表字段外键对应的表名称 </param>
159 /// <param name="information"> 表字段的描述信息 </param>
160 public ColumnAttribute( string name,
161 string tableName,
162 DataType dataType,
163 int length,
164 bool canNull,
165 object defaultValue,
166 bool isPrimaryKey,
167 bool autoIncrement,
168 bool isUnique,
169 string regularExpress,
170 bool isForeignKey,
171 string foreignTabName,
172 string information)
173 {
174 this .name = name;
175 this .tableName = tableName;
176 this .dataType = dataType;
177 this .length = length;
178 this .canNull = canNull;
179 this .defaultValue = defaultValue;
180 this .isPrimaryKey = isPrimaryKey;
181 this .autoIncrement = autoIncrement;
182 this .regularExpress = regularExpress;
183 this .isForeignKey = isForeignKey;
184 this .ForeignTabName = foreignTabName;
185 this .information = information;
186 this .isUnique = isUnique;
187 }
188
189 /// <summary>
190 /// 表字段名称
191 /// </summary>
192 public string Name
193 {
194 get { return name; }
195 set { name = value; }
196 }
197
198 /// <summary>
199 /// 表字段对应表名称
200 /// </summary>
201 public string TableName
202 {
203 get { return tableName; }
204 set { tableName = value; }
205 }
206
207 /// <summary>
208 /// 表字段的数据类型
209 /// </summary>
210 public DataType DataType
211 {
212 get { return dataType; }
213 set { dataType = value; }
214 }
215
216 /// <summary>
217 /// 表字段的长度
218 /// </summary>
219 public int Length
220 {
221 get { return length; }
222 set { length = value; }
223 }
224
225 /// <summary>
226 /// 表字段是否可以为空
227 /// </summary>
228 public bool CanNull
229 {
230 get { return canNull; }
231 set { canNull = value; }
232 }
233
234 /// <summary>
235 /// 表字段的默认值
236 /// </summary>
237 public object DefaultValue
238 {
239 get { return defaultValue; }
240 set { defaultValue = value; }
241 }
242
243 /// <summary>
244 /// 表字段是否为主键
245 /// </summary>
246 public bool IsPrimaryKey
247 {
248 get { return isPrimaryKey; }
249 set { isPrimaryKey = value; }
250 }
251
252 /// <summary>
253 /// 表字段是否为自动增长列
254 /// </summary>
255 public bool AutoIncrement
256 {
257 get { return autoIncrement; }
258 set { autoIncrement = value; }
259 }
260
261 /// <summary>
262 /// 表字段的匹配规则
263 /// </summary>
264 public string RegularExpress
265 {
266 get { return regularExpress; }
267 set { regularExpress = value; }
268 }
269
270 /// <summary>
271 /// 表字段是否为外键
272 /// </summary>
273 public bool IsForeignKey
274 {
275 get { return isForeignKey; }
276 set { isForeignKey = value; }
277 }
278
279 /// <summary>
280 /// 表字段外键对应的表名称
281 /// </summary>
282 public string ForeignTabName
283 {
284 get { return foreignTabName; }
285 set { foreignTabName = value; }
286 }
287
288 /// <summary>
289 /// 表字段的描述信息
290 /// </summary>
291 public string Information
292 {
293 get { return information; }
294 set { information = value; }
295 }
296
297 /// <summary>
298 /// 确定某个字段是否唯一
299 /// </summary>
300 public bool IsUnique
301 {
302 get { return isUnique; }
303 set { isUnique = value; }
304 }
305 }
306 }
307
特性说明:实体表特性,用于描述实体字段和数据库表字段之间的映射关系,该特性类使用 sealed 修改,说明该类不能够再被继承。
LinkTableAttribute 特性
字段名 |
作用 |
name |
该实体属性对应的数据库表名 |
sqlPrefix |
该实体属性对应的数据库表名前缀 一般情况下没有太大意思 |
dataType |
该实体属性对应的数据类型 可以通过发射获得类型 |
keyName |
该实体对应的主键类型 (用数据的列名来表示,不要使用实体的属性来定义) |
className |
对应的实体的全路径类型 |
isLazy |
对应的实体是否延长加载 |
|
|
特性源码:
1 /**
3 *
4 * 情 缘
5 *
6 * 实体属性特性,用于描述某个实体作为另外一个实体
7 * 的一个属性字段的时候与数据库表之间的关系
8 *
9 * 这个实体特性类和其余的几个有区别,没有用sealed
10 * 修饰,因为这个特性是用于修饰实体属性的,而实体属
11 * 性的集合也是一种特殊的属性,所以可以使用
12 * LinkTablesAttribute 来继承该特性。两者在属性描述
13 * 上基本没有太大的区别
14 *
15 * */
16
17 using System;
18
19
20 namespace CommonData.Model.Core
21 {
22 public class LinkTableAttribute:Attribute
23 {
24 /// <summary>
25 /// 该实体属性对应的数据库表名
26 /// </summary>
27 private string name;
28
29 /// <summary>
30 /// 该实体属性对应的数据库表名前缀
31 /// 一般情况下没有太大意思
32 /// </summary>
33 private string sqlPrefix;
34
35 /// <summary>
36 /// 该实体属性对应的数据类型
37 /// 可以通过发射获得类型
38 /// </summary>
39 private Type dataType;
40
41 /// <summary>
42 /// 该实体对应的主键类型
43 /// (用数据的列名来表示,不要使用实体的属性来定义)
44 /// </summary>
45 private string keyName;
46
47 /// <summary>
48 /// 对应的实体的全路径类型
49 /// </summary>
50 private string className;
51
52 /// <summary>
53 /// 对应的实体是否延长加载
54 /// </summary>
55 private bool isLazy;
56
57 /// <summary>
58 /// 无参数构造方法
59 /// </summary>
60 public LinkTableAttribute()
61 {
62 }
63
64 /// <summary>
65 /// 部分参数构造方法,构造该特性实例的时候
66 /// 初始化部分属性
67 /// </summary>
68 /// <param name="name"> 该实体属性对应的数据库表名 </param>
69 /// <param name="dataType"> 该实体属性对应的数据类型 </param>
70 /// <param name="keyName"> 该实体对应的主键类型 </param>
71 /// <param name="className"> 对应的实体的全路径类型 </param>
72 public LinkTableAttribute( string name, Type dataType, string keyName, string className)
73 {
74 this .name = name;
75 this .dataType = dataType;
76 this .keyName = keyName;
77 this .className = className;
78 }
79
80 /// <summary>
81 /// 全参数构造方法,构造该特性实例的时候
82 /// 初始化全部属性
83 /// </summary>
84 /// <param name="name"> 该实体属性对应的数据库表名 </param>
85 /// <param name="sqlPrefix"> 该实体属性对应的数据库表名前缀 </param>
86 /// <param name="dataType"> 该实体属性对应的数据类型 </param>
87 /// <param name="keyName"> 该实体对应的主键类型 </param>
88 /// <param name="className"> 对应的实体的全路径类型 </param>
89 public LinkTableAttribute( string name, string sqlPrefix, Type dataType, string keyName, string className)
90 {
91 this .name = name;
92 this .sqlPrefix = sqlPrefix;
93 this .dataType = dataType;
94 this .keyName = keyName;
95 this .className = className;
96 }
97
98 public string Name
99 {
100 get { return name; }
101 set { name = value; }
102 }
103
104
105 public string SqlPrefix
106 {
107 get { return sqlPrefix; }
108 set { sqlPrefix = value; }
109 }
110
111
112 public Type DataType
113 {
114 get { return dataType; }
115 set { dataType = value; }
116 }
117
118
119 public string KeyName
120 {
121 get { return keyName; }
122 set { keyName = value; }
123 }
124
125
126 public string ClassName
127 {
128 get { return className; }
129 set { className = value; }
130 }
131
132 public bool IsLazy
133 {
134 get { return isLazy; }
135 set { isLazy = value; }
136 }
137 }
138 }
139
特性说明:实体属性特性,用于描述某个实体作为另外一个实体的一个属性字段的时候与数据库表之间的关系.这个实体特性类和其余的几个有区别,没有用sealed 修饰,因为这个特性是用于修饰实体属性的,而实体属 性的集合也是一种特殊的属性,所以可以使用LinkTablesAttribute 来继承该特性。两者在属性描述上基本没有太大的区别
LinkTablesAttribute 特性
特性源码:
代码
2 * 2010-1-28
3 *
4 * 情 缘
5 *
6 * 实体集合属性特性,用于描述某个实体作为另外一个实体
7 * 的一个集合属性的时候与数据库表之间的关系
8 *
9 * 这个特性使用了 sealed 修饰,说明该特性类不能再被继
10 * 承。但是这个实体类不是在继承Attribute,而是继承
11 * LinkTableAttribute。因为这两者描述的属性具有相同的
12 * 特性。只需要让它继承LinkTableAttribute 保留父类的
13 * 属性就可以了。 LinkTablesAttribute,LinkTableAttribute
14 * 这两个特性类具有相同的属性,使用不同的类名只是为了
15 * 区分修饰的属性形态有所不同
16 *
17 * */
18
19 using System;
20
21 namespace CommonData.Model.Core
22 {
23 public sealed class LinkTablesAttribute:LinkTableAttribute
24 {
25 /// <summary>
26 /// 无参数构造方法
27 /// </summary>
28 public LinkTablesAttribute()
29 {
30 }
31
32 /// <summary>
33 /// 部分参数构造方法,构造该特性实例的时候,
34 /// 初始化部分属性,并且调用的是父类的构造
35 /// 方法
36 /// </summary>
37 /// <param name="name"> 该实体属性对应的数据库表名 </param>
38 /// <param name="dataType"> 该实体属性对应的数据类型 </param>
39 /// <param name="keyName"> 该实体对应的主键类型 </param>
40 /// <param name="className"> 对应的实体的全路径类型 </param>
41 public LinkTablesAttribute( string name, Type dataType, string keyName, string className)
42 : base (name,dataType,keyName,className)
43 { }
44
45
46 /// <summary>
47 /// 全参数构造方法,构造该特性实例的时候
48 /// 初始化全部属性 ,并且调用的是父类的构造
49 /// 方法
50 /// </summary>
51 /// <param name="name"> 该实体属性对应的数据库表名 </param>
52 /// <param name="sqlPrefix"> 该实体属性对应的数据库表名前缀 </param>
53 /// <param name="dataType"> 该实体属性对应的数据类型 </param>
54 /// <param name="keyName"> 该实体对应的主键类型 </param>
55 /// <param name="className"> 对应的实体的全路径类型 </param>
56 public LinkTablesAttribute( string name, string sqlPrefix, Type dataType, string keyName, string className)
57 : base (name,sqlPrefix,dataType,keyName,className)
58 {
59
60 }
61 }
62 }
63
特性说明:实体集合属性特性,用于描述某个实体作为另外一个实体的一个集合属性的时候与数据库表之间的关系
这个特性使用了 sealed 修饰,说明该特性类不能再被继承。但是这个实体类不是在继承Attribute,而是继承LinkTableAttribute。 因为这两者描述的属性具有相同的特性。只需要让它继承LinkTableAttribute 保留父类的属性就可以了。 LinkTablesAttribute,LinkTableAttribute这两个特性类具有相同的属性,使用不同的类名只是为了区分修饰的属性形态有所不同
源码点击下载
(注: ORM涉及内容比较多,后续期待。本文只是一个开端,有兴趣的可以与本人探讨)