ORM映射框架总结--SQL 语句生成组件

 1.       SQL 语句生成组建

之前说过ORM框架中有一个非常重要的组件,那就是SQL语句生成组件。SQL语句生成组件,顾名思义就是用于生成sql语句的一个组件。之前的Attribute 是用于修饰实体信息,而上一章讲的实体分析器分析了实体信息,在很大一部分程度上,以上做工作就是为这个SQL语句生成组件服务的。

该组件的核心接口是IDbFactory,它实现了接口IDisposable

目前该ORM支持SQL Server 数据库的sql语句生成,在后期过程中会逐步实现对Oracle,Mysql,Access 等数据库的支持

下面是该接口定义的方法图表:ORM映射框架总结--SQL 语句生成组件

2.       核心接口

核心接口定义源码

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件核心接口定义源码
  1 /**
  2  * 2010-2-2
  3  * 
  4  * 情 缘
  5  * 
  6  * IDbFactory 接口实现了IDisposable 接口,实现该
  7  * 接口的类可以通过IDisposable 接口来释放对象占用
  8  * 的内存。该接口的主要作用适用于根据实体对象来创
  9  * 建SQL Server 数据库脚本。ORM 的主要作用是为了
 10  * 实现对象化操作数据库,而此操作就是为了实现对象
 11  * 化操作和数据库语句操作的过渡
 12  * 
 13  * */
 14 using System;
 15 using System.Collections.Generic;
 16 using System.Linq;
 17 using System.Text;
 18 using CommonData.Entity;
 19 using System.Data;
 20 using CommonData.Model.Core;
 21 
 22 namespace CommonData.Data.Core
 23 {
 24     public interface IDbFactory : IDisposable
 25     {
 26         /// <summary>
 27         /// 根据实体对象公共接口创建插入的sql语句
 28         /// </summary>
 29         /// <param name="entity">实体公共接口</param>
 30         /// <param name="param">创建sql语句对应占位符参数</param>
 31         /// <returns></returns>
 32         string CreateInsertSql(IEntity entity, out IDataParameter[] param);
 33 
 34         /// <summary>
 35         /// 根据实体类型创建插入sql语句
 36         /// </summary>
 37         /// <param name="type">实体类型</param>
 38         /// <param name="value">实体对象</param>
 39         /// <param name="param">创建sql语句对应占位符参数</param>
 40         /// <returns></returns>
 41         string CreateInsertSql(Type type, object value, out IDataParameter[] param);
 42 
 43         /// <summary>
 44         /// 根据泛型类型创建插入sql语句
 45         /// </summary>
 46         /// <typeparam name="T">泛型类型</typeparam>
 47         /// <param name="t">泛型实体类</param>
 48         /// <param name="param">创建sql语句对应占位符参数</param>
 49         /// <returns></returns>
 50         string CreateInsertSql<T>(T t, out IDataParameter[] param) where T : IEntity;
 51 
 52         /// <summary>
 53         /// 根据实体对象公共接口创建修改的的sql语句
 54         /// 该sql语句是根据主键列修改的
 55         /// </summary>
 56         /// <param name="entity">实体公共接口</param>
 57         /// <param name="param">创建sql语句对应占位符参数</param>
 58         /// <returns></returns>
 59         string CreateUpdateSql(IEntity entity, out IDataParameter[] param);
 60 
 61         /// <summary>
 62         /// 根据实体对象类型创建修改的的sql语句
 63         /// </summary>
 64         /// <param name="type">实体类型</param>
 65         /// <param name="value">实体对象</param>
 66         /// <param name="param">创建sql语句对应占位符参数</param>
 67         /// <returns></returns>
 68         string CreateUpdateSql(Type type, object value, out IDataParameter[] param);
 69 
 70         /// <summary>
 71         /// 根据实体对象公共接口创建修改的的sql语句
 72         /// 该sql语句是根据一个特定的属性作为修改条件的
 73         /// </summary>
 74         /// <param name="entity">实体公共接口</param>
 75         /// <param name="param">创建sql语句对应占位符参数</param>
 76         /// <param name="propertyName">属性名称</param>
 77         /// <returns></returns>
 78         string CreateUpdateSql(IEntity entity, out IDataParameter[] param, string propertyName);
 79 
 80         /// <summary>
 81         /// 根据实体对象类型创建修改的的sql语句
 82         /// 该sql语句是根据一个特定的属性作为修改条件的
 83         /// </summary>
 84         /// <param name="type">实体类型</param>
 85         /// <param name="value">实体对象</param>
 86         /// <param name="param">创建sql语句对应占位符参数</param>
 87         /// <param name="propertyName">属性名称</param>
 88         /// <returns></returns>
 89         string CreateUpdateSql(Type type, object value, out IDataParameter[] param, string propertyName);
 90 
 91         /// <summary>
 92         /// 根据实体对象公共接口创建修改的的sql语句
 93         /// 该sql语句是根据多个特定的属性作为修改条件的
 94         /// </summary>
 95         /// <param name="entity">实体公共接口</param>
 96         /// <param name="param">创建sql语句对应占位符参数</param>
 97         /// <param name="propertyNames">属性名称</param>
 98         /// <returns></returns>
 99         string CreateUpdateSql(IEntity entity, out IDataParameter[] param, string[] propertyNames);
100 
101         /// <summary>
102         /// 根据实体对象类型创建修改的的sql语句
103         /// 该sql语句是根据多个特定的属性作为修改条件的
104         /// </summary>
105         /// <param name="type">实体类型</param>
106         /// <param name="value">实体对象</param>
107         /// <param name="param">创建sql语句对应占位符参数</param>
108         /// <param name="propertyNames">属性名称</param>
109         /// <returns></returns>
110         string CreateUpdateSql(Type type, object value, out IDataParameter[] param, string[] propertyNames);
111 
112         /// <summary>
113         /// 根据实体对象公共接口创建修改的的sql语句
114         /// 该sql语句是根据查询组建创建的
115         /// </summary>
116         /// <param name="entity">实体公共接口</param>
117         /// <param name="param">创建sql语句对应占位符参数</param>
118         /// <param name="component">查询条件组件</param>
119         /// <returns></returns>
120         string CreateUpdateSql(IEntity entity, out IDataParameter[] param, ConditionComponent component);
121 
122         /// <summary>
123         /// 根据实体对象公共接口创建删除sql语句
124         /// 该sql语句是根据实体主键删除
125         /// </summary>
126         /// <param name="entity">实体公共接口</param>
127         /// <param name="param">创建sql语句对应占位符参数</param>
128         /// <returns></returns>
129         string CreateDeleteSql(IEntity entity, out IDataParameter[] param);
130 
131         /// <summary>
132         /// 根据实体对象类型创建删除sql语句
133         /// 该sql语句是根据实体主键删除
134         /// </summary>
135         /// <param name="type">实体类型</param>
136         /// <param name="value">实体对象</param>
137         /// <param name="param">创建sql语句对应占位符参数</param>
138         /// <returns></returns>
139         string CreateDeleteSql(Type type,object value,out IDataParameter[] param);
140 
141         /// <summary>
142         /// 根据实体对象公共接口的某个属性创建删除sql语句
143         /// 该sql语句是根据实体属性删除
144         /// </summary>
145         /// <param name="entity">实体公共接口</param>
146         /// <param name="param">创建sql语句对应占位符参数</param>
147         /// <param name="propertyName">实体属性名称</param>
148         /// <returns></returns>
149         string CreateDeleteSql(IEntity entity, out IDataParameter[] param, string propertyName);
150 
151         /// <summary>
152         /// 根据实体对象类型的某个属性创建删除sql语句
153         /// 该sql语句是根据实体属性删除
154         /// </summary>
155         /// <param name="type">实体类型</param>
156         /// <param name="value">实体对象</param>
157         /// <param name="param">创建sql语句对应占位符参数</param>
158         /// <param name="propertyName">实体属性名称</param>
159         /// <returns></returns>
160         string CreateDeleteSql(Type type, object value, out IDataParameter[] param, string propertyName);
161 
162         /// <summary>
163         /// 根据实体对象公共接口的多个属性创建删除sql语句
164         /// 该sql语句是根据实体多个属性删除
165         /// </summary>
166         /// <param name="entity">实体公共接口</param>
167         /// <param name="param">创建sql语句对应占位符参数</param>
168         /// <param name="propertyName">实体属性名称数组</param>
169         /// <returns></returns>
170         string CreateDeleteSql(IEntity entity, out IDataParameter[] param, string[] propertyNames);
171 
172         /// <summary>
173         /// 根据实体对象类型的多个属性创建删除sql语句
174         /// 该sql语句是根据实体多个属性删除
175         /// </summary>
176         /// <param name="type">实体了姓</param>
177         /// <param name="value">实体对象</param>
178         /// <param name="param">创建sql语句对应占位符参数</param>
179         /// <param name="propertyNames">实体属性名称数组</param>
180         /// <returns></returns>
181         string CreateDeleteSql(Type type, object value, out IDataParameter[] param, string[] propertyNames);
182 
183         /// <summary>
184         /// 根据实体对象公共接口的多个属性创建删除sql语句
185         /// 该sql语句使根据查询组建来创建的
186         /// </summary>
187         /// <param name="entity">实体公共接口</param>
188         /// <param name="param">创建sql语句对应占位符参数</param>
189         /// <param name="component">实体属性名称数组</param>
190         /// <returns></returns>
191         string CreateDeleteSql(IEntity entity, out IDataParameter[] param, ConditionComponent component);
192 
193         /// <summary>
194         /// 根据实体的公共接口创建查询单行数据的sql语句
195         /// 该sql语句是根据数据库表的主键来查询的
196         /// </summary>
197         /// <param name="entity">实体公共接口</param>
198         /// <param name="param">创建sql语句对应占位符参数</param>
199         /// <returns></returns>
200         string CreateSingleSql(IEntity entity, out IDataParameter[] param);
201 
202         /// <summary>
203         /// 根据实体的公共接口创建查询单行数据的sql语句
204         /// 该sql语句是根据实体的相应属性来查询
205         /// </summary>
206         /// <param name="entity">实体公共接口</param>
207         /// <param name="param">创建sql语句对应占位符参数</param>
208         /// <returns></returns>
209         string CreateSingleSql(IEntity entity, out IDataParameter[] param, string[] propertyNames);
210 
211         /// <summary>
212         /// 根据实体类型创建查询单行数据的sql语句
213         /// 该sql语句是根据实体的相应属性来查询
214         /// </summary>
215         /// <param name="type">实体类型</param>
216         /// <param name="value">实体对象</param>
217         /// <param name="param">创建sql语句对应占位符参数</param>
218         /// <param name="propertyNames">属性名称数组</param>
219         /// <returns></returns>
220         string CreateSingleSql(Type type, object value, out IDataParameter[] param, string[] propertyNames);
221 
222         /// <summary>
223         /// 根据实体的类型创建查询sql语句
224         /// </summary>
225         /// <param name="entityType">实体类型</param>
226         /// <returns></returns>
227         string CreateSingleSql(Type entityType);
228 
229         /// <summary>
230         /// 根据实体的类型创建查询sql语句,
231         /// 该方法指定主键值
232         /// </summary>
233         /// <param name="type">实体类型</param>
234         /// <param name="pkPropertyValue">主键值</param>
235         /// <param name="param">创建sql语句对应占位符参数</param>
236         /// <returns></returns>
237         string CreateSingleSql(Type type, object pkPropertyValue,out IDataParameter[] param);
238 
239         /// <summary>
240         /// 根据实体的类型创建查询该实体对象对应数据库表的所有数据的sql语句
241         /// 该sql语句用于查询所有数据,并转换为相应List<T> 集合
242         /// </summary>
243         /// <param name="type">实体的类型</param>
244         /// <returns></returns>
245         string CreateQuerySql(Type type);
246 
247         /// <summary>
248         /// 根据实体的某个属性创建根据该属性字段查询数据的sql语句
249         /// 该sql语句是使用参数中属性对应字段作为条件查询的
250         /// </summary>
251         /// <param name="type">实体类型</param>
252         /// <param name="propertyName">属性名称</param>
253         /// <param name="value">属性值</param>
254         /// <param name="param">sql语句占位符参数</param>
255         /// <returns></returns>
256         string CreateQueryByPropertySql(Type type, string propertyName, object value, out IDataParameter[] param);
257 
258         /// <summary>
259         /// 根据实体的某些属性创建根据该些属性字段查询数据的sql语句
260         /// 该sql语句是使用参数中属性对应字段作为条件查询的,并且该
261         /// 属性集合都是根据and条件组装的
262         /// </summary>
263         /// <param name="type">实体类型</param>
264         /// <param name="dic">属性-值集合</param>
265         /// <param name="param">sql语句占位符参数</param>
266         /// <returns></returns>
267         string CreateQueryByPropertySql(Type type, IDictionary<stringobject> dic, out IDataParameter[] param);
268 
269         /// <summary>
270         /// 根据实体的某些属性创建根据该些属性字段查询数据的sql语句
271         /// 该sql语句是使用参数中属性对应字段作为条件查询的,并且查
272         /// 询是根据查询组建来创建
273         /// </summary>
274         /// <param name="type">实体类型</param>
275         /// <param name="dic">属性-值集合</param>
276         /// <param name="param">sql语句占位符参数</param>
277         /// <param name="component">查询组建</param>
278         /// <returns></returns>
279         string CreateQueryByPropertySql(Type type, IDictionary<stringobject> dic, out IDataParameter[] param, ConditionComponent component);
280 
281         /// <summary>
282         /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
283         /// 该方法创建的sql语句主要是用于查询数据行数
284         /// </summary>
285         /// <param name="type">实体类型</param>
286         /// <param name="converage">聚合函数枚举类型</param>
287         /// <returns></returns>
288         string CreateConverageSql(Type type, Converage converage);
289 
290         /// <summary>
291         /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
292         /// 该方法创建的sql语句主要是用于统计查询(最大值,最小值,求和,平均值,数据行数)
293         /// </summary>
294         /// <param name="type">实体类型</param>
295         /// <param name="converage">聚合函数枚举类型</param>
296         /// <param name="propertyName">聚合函数作用的属性名称</param>
297         /// <returns></returns>
298         string CreateConverageSql(Type type, Converage converage, string propertyName);
299 
300         /// <summary>
301         /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
302         /// 该方法创建的sql语句主要是用于统计查询(最大值,最小值,求和,平均值,数据行数),
303         /// 同时该sql是有条件查询的
304         /// </summary>
305         /// <param name="type">实体类型</param>
306         /// <param name="converage">聚合函数枚举类型</param>
307         /// <param name="propertyName">聚合函数作用的属性名称</param>
308         /// <param name="dic">查询条件属性键值</param>
309         /// <param name="component">查询条件组建对象</param>
310         /// <returns></returns>
311         string CreateConverageSql(Type type, Converage converage, string propertyName, IDictionary<stringobject> dic, out IDataParameter[] param, ConditionComponent component);
312 
313         /// <summary>
314         /// 根据占位符名称创建参数
315         /// </summary>
316         /// <param name="name">占位符名称</param>
317         /// <returns></returns>
318         IDataParameter CreateParameter(string name);
319 
320         /// <summary>
321         /// 根据占位符和值创建参数
322         /// </summary>
323         /// <param name="name">占位符名称</param>
324         /// <param name="value">占位符的值</param>
325         /// <returns></returns>
326         IDataParameter CreateParameter(string name, object value);
327 
328         /// <summary>
329         /// 根据占位符名称,类型和值创建参数
330         /// </summary>
331         /// <param name="name">占位符名称</param>
332         /// <param name="type">参数的类型</param>
333         /// <param name="value">参数的值</param>
334         /// <returns></returns>
335         IDataParameter CreateParameter(string name, DataType type, object value);
336 
337         /// <summary>
338         /// 根据占位符的名称,类型和大小创建参数
339         /// </summary>
340         /// <param name="name">占位符名称</param>
341         /// <param name="type">参数类型</param>
342         /// <param name="size">参数值大小</param>
343         /// <returns></returns>
344         IDataParameter CreateParameter(string name, DataType type, int size);
345 
346         /// <summary>
347         /// 根据占位符的名称,类型,大小和值创建参数
348         /// </summary>
349         /// <param name="name">占位符名称</param>
350         /// <param name="type">参数类型</param>
351         /// <param name="size">参数大小</param>
352         /// <param name="value">参数值</param>
353         /// <returns></returns>
354         IDataParameter CreateParameter(string name, DataType type, int size, object value);
355 
356         /// <summary>
357         /// 根据占位符名称和类型创建参数
358         /// </summary>
359         /// <param name="name">占位符名称</param>
360         /// <param name="type">占位符类型</param>
361         /// <returns></returns>
362         IDataParameter CreateParameter(string name, DataType type);
363     }
364 }
365 

 

 

  

   该接口定了生成增删改查等sql语句的方法

CreateInsertSql() 方法主要用于生成插入语句,该方法被重载了3次,根据各种不同的情况来生成数据库插入sql语句。

CreateUpdateSql() 方法主要用于生成修改语句,该方法被重载了7次,根据各种不同的情况来生成数据的修改sql语句。

CreateDeleteSql() 方法主要用于生成删除语句,该方法同样被重载了7,根据各种不同的情况来生成数据库的删除sql语句。

CreateSingleSql() 方法主要用于生成查询单个实体的sql语句,该方法被重载了5,根据各种不同的情况来生成数据库的单数据行sql语句。

CreateQuerySql() 方法主要用于生成查询集合的sql语句,该方法只被重载了一次,从上面的源码中可以看出,该方法只有一个Type类型参数,此方法不做正在意义上查询使用,此方法在配合级联查询的时候能够起到重要的作用。

CreateQueryByPropertySql() 方法主要生成条件查询的sql语句,该方法被充值了3,根据不同的情况来生成数据的集合查询的sql语句。注意该方法中引入了一个新的对象ConditionComponent在后面的说明中对次类进行讲解。

CreateConverageSql() 方法主要用于生成查询聚合函数的sql语句,该方法同样被重载了3次,根据不同的情况来生成查询聚合函数的sql语句。该方法同样也引入了一个新的对象Converage在后面的说明中对次类进行讲解。

CreateParameter() 方法主要用于生成sql语句占位符参数的,该方法被重载了6次。

以上是该核心接口定义的几组方法,用于生成sql语句。在程序设计的过程中,都采用的面向接口来编程,目前来说只支持sql server 数据的sql语句生成,但是考虑到后期的补充,觉得在使用接口编程就尤为重要。后期的工作就是根据不同的数据库去实现该接口中的所有方法。

3.       插入sql语句的生成

上面说到了插入sql语句被重载了3次,在很多情况下我们根据情况的不同生成sql语句所需的方式也不同,于是归纳成了三个重载方法,该三个重载放一般情况下是可以覆盖所有的生成插入语句的情况。

string CreateInsertSql(IEntity entity, out IDataParameter[] param);

在上面的桥梁映射过程中,我们提到了一个接口IEntity,它是所有实体的父级接口,该接口并未定义任何方法,或许当时有人认为这是多此一举,不过现在大家应该明白了,这样定义是有缘由的。因为这个插入语句的生成要覆盖所有的实体类,于是我们必须抽象出来,抽象它们共同拥有的特性。在这里,这个IEntity 接口的重要性显得尤为突出。Param参数这是用于存储sql语句的占位符的,看到这个参数的修饰符out,说明参数传入本身是空的,也就是说在后期的实现过程中我们要动态的去给param赋值,参数名,参数值,以及数组的长度都是由entity 来决定。

string CreateInsertSql(Type type, object value, out IDataParameter[] param);

说到重载,这是面向对象编程的四要素之一,多态表现形式之一,另外一种是重写。重载其实要实现的功能是一样,只是表现形式不同而已,这里使用了Type,这里就是要插入的实体的类型,后面的object value 就是实体,紧接着的参数就不用多说了,上面的方法已经提到过。

string CreateInsertSql<T>(T t, out IDataParameter[] param) where T : IEntity

说到这个重载方法,大家一般都会很兴奋,为什么.我第一眼看到了<T> .net中的泛型。.net中的泛型我可以说真的是无懈可击,那各种形态的使用方式是如此的婀娜多姿,总是让程序员沉迷于其中的美妙,最起码我是这样的。后面的where T : IEntity 就是泛型约束了,泛型约束可以让我们的程序减少很多不必要的麻烦。因为泛型本身就是一种不确定的类型,我们规定了他使用的范围,这样能够减少它出错的可能性。

下面看看这个插入sql的生成核心代码:

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件插入sql的生成核心代码
 1 /// <summary>
 2         /// 根据实体类型创建插入sql语句
 3         /// </summary>
 4         /// <param name="type">实体类型</param>
 5         /// <param name="value">实体对象</param>
 6         /// <param name="param">创建sql语句对应占位符参数</param>
 7         /// <returns></returns>
 8         public string CreateInsertSql(Type type, object value, out IDataParameter[] param)
 9         {
10             if (value == null)
11             {
12                 throw new NullReferenceException("the save entity is null");
13             }
14             StringBuilder sbColumn = new StringBuilder("");
15             StringBuilder sbValues = new StringBuilder("");
16             IDictionary<string, ColumnAttribute> dicColumn = EntityTypeCache.GetTableInfo(type).DicColumns;
17             if (dicColumn.Keys.Count > 0)
18             {
19                 sbColumn.AppendFormat("insert into {0} (", EntityTypeCache.GetTableInfo(type).Table.Name);
20                 sbValues.AppendFormat(" values (");
21                 IList<IDataParameter> listParams = new List<IDataParameter>();
22                 foreach (string key in dicColumn.Keys)
23                 {
24                     if (!dicColumn[key].AutoIncrement)
25                     {
26                         sbColumn.AppendFormat("{0},", dicColumn[key].Name);
27                         sbValues.AppendFormat("{0},""@" + dicColumn[key].Name);
28                     }
29                     if (EntityFactory.GetPropertyValue(value, key) == null)
30                     {
31                         listParams.Add(CreateParameter("@" + key, System.DBNull.Value));
32                     }
33                     else
34                     {
35                         listParams.Add(CreateParameter("@" + key, EntityFactory.GetPropertyValue(value, key)));
36                     }
37                 }
38                 sbColumn.Replace(","")", sbColumn.Length - 11);
39                 sbValues.Replace(","")", sbValues.Length - 11);
40                 param = listParams.ToArray();
41                 return sbColumn.ToString() + sbValues.ToString();
42             }
43             else
44             {
45                 param = null;
46                 return null;
47             }
48         }

 

 

 

    当大家看到这里肯定也觉得,原理这个东西也不过如此。如果真是这样,那我也就替你高兴了,因为我要传达的意思你已经明白了,也就是说我写这边博文的传达的信息你收到了。一次完美的会话完成。

    StringBuilder sbColumn = new StringBuilder("");

StringBuilder sbValues = new StringBuilder("");

代码中定义了这两个类,不使用string的原因大家也知道。还有这里我们生成sql语句的规则就是sql语句插入的语法规则,这里不再多说。

4.       修改sql语句

生成修改sql语句的方法CreateUpdateSql()被重载了七次,其实生成sql语句的原理和上面生成插入sql语句的原理是一样的。关键在于如何去抽象一个多情况下处理方式。

string CreateUpdateSql(IEntity entity, out IDataParameter[] param);

这个方法主要是根据实体的主键作为条件来修改

string CreateUpdateSql(IEntity entity, out IDataParameter[] param, string[] propertyNames);

这个方法则是根据实体的过个属性来修改,各个条件以and方式来连接生成sql语句。在生成sql语句的过程中IEntity 必须包含这些数据,而且这些属性值必须存在,否则会抛出异常。与此类似的方法还有只有一个属性名来修改的,其实这个方法可以包括只有一个属性的修改方法,而这里重载一次就是为了使用方便。

string CreateUpdateSql(IEntity entity, out IDataParameter[] param, ConditionComponent component);

这里我们不介绍其他东西了,主要看看ConditionComponent这个类。上面提到过如果多条件修改sql语句,各个属性都是通过and来连接的,如果我们修改使用 or 或者其他的方式怎么办,ConditionComponent可以用来解决此问题。

首先看看这个类的源码:

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件查询组件存储器源码
 1 /**
 2  * 2010-3-1
 3  * 
 4  * 情 缘
 5  * 
 6  * 该类封装修改数据,条件查询数据的相关条件。
 7  * 
 8  * */
 9 using System;
10 using System.Collections.Generic;
11 using System.Linq;
12 using System.Text;
13 
14 namespace CommonData.Model.Core
15 {
16     public class ConditionComponent
17     {
18         private IDictionary<string, SearchComponent> dicComponent = null;
19 
20         /// <summary>
21         /// 用于存储属性查询类型
22         /// </summary>
23         public IDictionary<string, SearchComponent> DicComponent
24         {
25             get { return dicComponent; }
26             set { dicComponent = value; }
27         }
28 
29         private static ConditionComponent component;
30 
31         /// <summary>
32         /// 私有构造方法,禁止外部类构造此类的实例
33         /// 使用私有构造方式主要实现单例模式
34         /// </summary>
35         private ConditionComponent()
36         { 
37         
38         }
39 
40         /// <summary>
41         /// 构造ConditionComponent的实例,当实例不存在是则创建该对象
42         /// 这个是单例模式的实现
43         /// </summary>
44         /// <returns></returns>
45         public static ConditionComponent Instance()
46         {
47             if (component == null)
48             {
49                 component = new ConditionComponent();
50                 component.DicComponent = new Dictionary<string, SearchComponent>();
51             }
52             return component;
53         }
54 
55         /// <summary>
56         /// 添加属性查询类型
57         /// </summary>
58         /// <param name="propertyName">属性名称</param>
59         /// <param name="component">查询类型</param>
60         /// <returns></returns>
61         public ConditionComponent AddComponent(string propertyName,SearchComponent component)
62         {
63             if (component == null)
64             {
65                 ConditionComponent.component = Instance();
66             }
67             ConditionComponent.component.DicComponent.Add(propertyName, component);
68             return ConditionComponent.component;
69         }
70 
71     }
72 }
73 

 

 

 

   这个类其实就相当于一个存储器。

private IDictionary<string, SearchComponent> dicComponent = null;

dicComponent就是用于存储相应的数据的,而且是以键值对的方式存储,这样便于后面使用过程中的查找。而这里又出现了一个心得类SearchComponent,这个是查询条件组建。下面在讲解。这里定义了这个类的一个单例模式,也就是说只能允许一个对象的存在每次。我需要在每次天际参数的同时这个对象仍然存在,并且保存信息。

    public ConditionComponent AddComponent(string propertyName,SearchComponent component)

        {

            if (component == null)

            {

                ConditionComponent.component = Instance();

            }

            ConditionComponent.component.DicComponent.Add(propertyName, component);

            return ConditionComponent.component;

     }

这个方法每次给ConditionComponent添加一个参数,然后又返回这个对象。

下面看看上面的提到的SearchComponent源码:

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件查询组件枚举
 1 /**
 2  * 2010-3-1
 3  * 
 4  * 情 缘
 5  * 
 6  * 该类是一个枚举类型,定义了元素查询条件。
 7  * 该枚举值都一一对应数据库中的各种查询条
 8  * 件。
 9  * 
10  * */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Text;
15 
16 namespace CommonData.Model.Core
17 {
18     public enum SearchComponent
19     {
20         /// <summary>
21         /// 对应数据库中的 "="
22         /// </summary>
23         Equals,
24 
25         /// <summary>
26         /// 对应数据库中的 "!="
27         /// </summary>
28         UnEquals,
29 
30         /// <summary>
31         /// 对应数据库中的 ">"
32         /// </summary>
33         Greater,
34 
35         /// <summary>
36         /// 对应数据库中的 ">="
37         /// </summary>
38         GreaterOrEquals,
39 
40         /// <summary>
41         /// 对应数据库中的 "<"
42         /// </summary>
43         Less,
44         
45         /// <summary>
46         /// 对应数据库中的 "<="
47         /// </summary>
48         LessOrEquals,
49 
50         /// <summary>
51         /// 对应数据库中的 "like"
52         /// </summary>
53         Like,
54 
55         /// <summary>
56         /// 对应数据库中的 "in"
57         /// </summary>
58         In,
59 
60         /// <summary>
61         /// 对应数据库中的 "between and"
62         /// </summary>
63         Between,
64 
65         /// <summary>
66         /// 对应数据库中的 "order by asc"
67         /// </summary>
68         OrderAsc,
69 
70         /// <summary>
71         /// 对应数据库中的 "order by desc"
72         /// </summary>
73         OrderDesc,
74 
75         /// <summary>
76         /// 对应数据库中的 "group by"
77         /// </summary>
78         GroupBy,
79 
80         /// <summary>
81         /// 对应数据库中的 "or"
82         /// </summary>
83         Or
84     }
85 }
86 

 

 

这里的代码大家一看也就明白了,为什么叫查询组件了,它其实就是封装了一些查询可能出现的情况。代码非常简单,这里不再过多讲解。

下面是生成修改语句的方法实现,这个方法的代码包含了查询组件的运用:

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件修改sql语句的实现
 1 /// <summary>
 2         /// 根据实体对象公共接口创建修改的的sql语句
 3         /// 该sql语句是根据查询组建创建的
 4         /// </summary>
 5         /// <param name="entity">实体公共接口</param>
 6         /// <param name="param">创建sql语句对应占位符参数</param>
 7         /// <param name="component">查询条件组件</param>
 8         /// <returns></returns>
 9         public string CreateUpdateSql(IEntity entity, out IDataParameter[] param, ConditionComponent component)
10         {
11 
12             StringBuilder sbColumn = new StringBuilder("");
13             StringBuilder sbValues = new StringBuilder("");
14             IList<IDataParameter> listParams = new List<IDataParameter>();
15             sbColumn.AppendFormat("update {0} set ", EntityTypeCache.GetTableInfo(entity).Table.Name);
16             sbValues.Append(" where 1=1 ");
17             foreach (string propertyName in EntityTypeCache.GetTableInfo(entity).DicProperties.Keys)
18             {
19                 //包含则作为条件
20                 if (component.DicComponent.Keys.Contains(propertyName))
21                 {
22                     switch (component.DicComponent[propertyName])
23                     {
24                         case SearchComponent.Equals:
25                             sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
26                             break;
27                         case SearchComponent.UnEquals:
28                             sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "!=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
29                             break;
30                         case SearchComponent.Between:
31                             break;
32                         case SearchComponent.Greater:
33                             sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, ">", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
34                             break;
35                         case SearchComponent.GreaterOrEquals:
36                             sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, ">=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
37                             break;
38                         case SearchComponent.GroupBy:
39                             break;
40                         case SearchComponent.In:
41                             break;
42                         case SearchComponent.Less:
43                             sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "<", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
44                             break;
45                         case SearchComponent.LessOrEquals:
46                             sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, "<=", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
47                             break;
48                         case SearchComponent.Like:
49                             break;
50                         case SearchComponent.Or:
51                             break;
52                         case SearchComponent.OrderAsc:
53                             break;
54                         case SearchComponent.OrderDesc:
55                             break;
56                     }
57                     listParams.Add(CreateParameter("@" + EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, EntityFactory.GetPropertyValue(entity, propertyName) == null ? DBNull.Value : EntityFactory.GetPropertyValue(entity, propertyName)));
58                 }
59                 else  //判断主键和唯一列,主键和唯一列不能被修改
60                 {
61                     if (EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].IsPrimaryKey ||
62                         EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].IsUnique ||
63                         EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].AutoIncrement)
64                     {
65 
66                     }
67                     else
68                     {
69                         sbColumn.AppendFormat("{0}=@{1},", EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name);
70                         listParams.Add(CreateParameter("@" + EntityTypeCache.GetTableInfo(entity).DicColumns[propertyName].Name, EntityFactory.GetPropertyValue(entity, propertyName) == null ? DBNull.Value : EntityFactory.GetPropertyValue(entity, propertyName)));
71                     }
72                 }
73             }
74             sbColumn.Remove(sbColumn.Length - 11);
75             param = listParams.ToArray();
76             return sbColumn.ToString() + sbValues.ToString();
77         }

 

 

5.       聚合函数的使用

这里不做过多的降解了,都是前篇一律的,不过有个特殊的地方,那就是刚才提到过来的Converage

先看看这个枚举的源码:

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件聚合函数Converage枚举
 1 /**
 2  * 2010-2-26
 3  * 
 4  * 情 缘
 5  * 
 6  * 该类是一个枚举类型,定义了数据库聚合函数
 7  * 操作的各种情况。该枚举值可以在使用时候来
 8  * 区分sql执行那个聚合函数
 9  * 
10  * */
11 using System;
12 using System.Collections.Generic;
13 using System.Linq;
14 using System.Text;
15 
16 namespace CommonData.Model.Core
17 {
18     public enum Converage
19     {
20         /// <summary>
21         /// 聚合函数取最小值
22         /// </summary>
23         Min,
24 
25         /// <summary>
26         /// 聚合函数取最大值
27         /// </summary>
28         Max,
29 
30         /// <summary>
31         /// 聚合函数取和
32         /// </summary>
33         Sum,
34 
35         /// <summary>
36         /// 聚合函数取所有数据行
37         /// </summary>
38         Count,
39 
40         /// <summary>
41         /// 聚合函数取所有非空数据行
42         /// </summary>
43         CountNotNll,
44 
45         /// <summary>
46         /// 聚合函数取平均值
47         /// </summary>
48         Avg,
49     }
50 }
51 

 

 

  

该代码中定义了最和函数取最大值和最小值,聚合函数求和,聚合函数查询行数,聚合函数求平均值。

下面是聚合函数sql语句生成的代码实现:

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件聚合函数sql语句生成的代码实现
 1 /// <summary>
 2         /// 根据实体类型来创建该实体对应数据库表的聚合函数查询sql语句
 3         /// 该方法创建的sql语句主要是用于统计查询(最大值,最小值,求和,平均值,数据行数),
 4         /// 同时该sql是有条件查询的
 5         /// </summary>
 6         /// <param name="type">实体类型</param>
 7         /// <param name="converage">聚合函数枚举类型</param>
 8         /// <param name="propertyName">聚合函数作用的属性名称</param>
 9         /// <param name="dic">查询条件属性键值</param>
10         /// <param name="component">查询条件组建对象</param>
11         /// <returns></returns>
12         public string CreateConverageSql(Type type, Converage converage, string propertyName, IDictionary<stringobject> dic, out IDataParameter[] param, ConditionComponent component)
13         {
14             StringBuilder sbValues = new StringBuilder();
15             if (string.IsNullOrEmpty(propertyName))
16             {
17                 converage = Converage.Count;
18             }
19             if (Converage.Avg == converage)
20             {
21                 sbValues.AppendFormat("select avg({0}) from {1} where 1=1 ", EntityTypeCache.GetTableInfo(type).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(type).Table.Name);
22             }
23             else if (Converage.Max == converage)
24             {
25                 sbValues.AppendFormat("select max({0}) from {1} where 1=1 ", EntityTypeCache.GetTableInfo(type).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(type).Table.Name);
26             }
27             else if (Converage.Min == converage)
28             {
29                 sbValues.AppendFormat("select min({0}) from {1} where 1=1 ", EntityTypeCache.GetTableInfo(type).DicColumns[propertyName].Name, EntityTypeCache.GetTableInfo(type).Table.Name);
30             }
31             else
32             {
33                 sbValues.AppendFormat("select count(*) from {0} where 1=1 ", EntityTypeCache.GetTableInfo(type).Table.Name);
34             }
35 
36             IList<IDataParameter> listParams = new List<IDataParameter>();
37             foreach (string key in dic.Keys)
38             {
39                 switch (component.DicComponent[key])
40                 {
41                     case SearchComponent.Equals:
42                         sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
43                         break;
44                     case SearchComponent.UnEquals:
45                         sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "!=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
46                         break;
47                     case SearchComponent.Between:
48                         break;
49                     case SearchComponent.Greater:
50                         sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, ">", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
51                         break;
52                     case SearchComponent.GreaterOrEquals:
53                         sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, ">=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
54                         break;
55                     case SearchComponent.GroupBy:
56                         break;
57                     case SearchComponent.In:
58                         break;
59                     case SearchComponent.Less:
60                         sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "<", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
61                         break;
62                     case SearchComponent.LessOrEquals:
63                         sbValues.AppendFormat("and {0}{1}@{2} ", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, "<=", EntityTypeCache.GetTableInfo(type).DicColumns[key].Name);
64                         break;
65                     case SearchComponent.Like:
66                         break;
67                     case SearchComponent.Or:
68                         break;
69                     case SearchComponent.OrderAsc:
70                         break;
71                     case SearchComponent.OrderDesc:
72                         break;
73                 }
74                 listParams.Add(CreateParameter("@" + EntityTypeCache.GetTableInfo(type).DicColumns[key].Name, dic[key]));
75             }
76             param = listParams.ToArray();
77             return sbValues.ToString();
78         }

 

 

6.       参数占位符

在这个生成sql语句的组建方法中,我们不断使用了IDataParameter 这个对象,这个对象或许我们看了很陌生,我们用得非常少.(纠正一下是接口),在查询sql Server 数据的时候我们经常使用的一个类 SqlParameter,看到这两个名称如此相似,我们应该也想到了他们是什么关系。因为我们不确定是使用什么数据库,可能是Sql Server,可能是Oracle,还有肯能是Access,所以使用IDataParameter 是比较保险的一种方式。在这个方法中我们也看到了一个新的类型DataType它定义了数据库中常用25中类型于.net中数据类型的对应列表。

 下面是源码解析:

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件数据类型对应枚举列表
  1 /**
  2  * 2010-1-28
  3  * 
  4  * 情 缘
  5  * 
  6  * 用于描述SQL数据库类型和.NET 中数据类型的转化关系
  7  * 注意: 这里的数据类型并不是所有的都能直接转化,有
  8  * 时候需要通过特定的规则进行强制性转化。这里描述的
  9  * 都是SQL Server 数据的类型。
 10  * */
 11 
 12 using System;
 13 
 14 
 15 namespace CommonData.Model.Core
 16 {
 17     public enum DataType
 18     {
 19         /// <summary>
 20         /// 对应.NET中的数据类型 Int64 
 21         /// </summary>
 22         Bigint,            
 23 
 24         /// <summary>
 25         /// 对应.NET中的数据类型 Int32 
 26         /// </summary>
 27         Int,              
 28 
 29         /// <summary>
 30         /// 对应.NET中的数据类型 Int16 
 31         /// </summary>
 32         Smallint,         
 33 
 34         /// <summary>
 35         /// 对应.NET中的数据类型 System.Byte 
 36         /// </summary>
 37         Tinyint,          
 38 
 39         /// <summary>
 40         /// 对应.NET中的数据类型 bool 
 41         /// </summary>
 42         Bit,              
 43 
 44         /// <summary>
 45         /// 对应.NET中的数据类型 System.Decimal 
 46         /// </summary>
 47         Decimal,          
 48 
 49         /// <summary>
 50         /// 对应.NET中的数据类型 System.Decimal 
 51         /// </summary>
 52         Numeric,         
 53 
 54         /// <summary>
 55         /// 对应.NET中的数据类型 System.Decimal 
 56         /// </summary>
 57         Money,             
 58 
 59         /// <summary>
 60         /// 对应.NET中的数据类型 
 61         /// </summary>
 62         Smallmoney,        
 63 
 64         /// <summary>
 65         /// 对应.NET中的数据类型 System.Double 
 66         /// </summary>
 67         Float,            
 68 
 69         /// <summary>
 70         /// 对应.NET中的数据类型 System.Single 
 71         /// </summary>
 72         Real,             
 73 
 74         /// <summary>
 75         /// 对应.NET中的数据类型 System.DateTime 
 76         /// </summary>
 77         Datetime,          
 78 
 79         /// <summary>
 80         /// 对应.NET中的数据类型 System.DateTime 
 81         /// </summary>
 82         Smalldatetime,     
 83 
 84         /// <summary>
 85         /// 对应.NET中的数据类型 String 
 86         /// </summary>
 87         Char,             
 88 
 89         /// <summary>
 90         /// 对应.NET中的数据类型 String 
 91         /// </summary>
 92         Varchar,          
 93 
 94         /// <summary>
 95         /// 对应.NET中的数据类型 String 
 96         /// </summary>
 97         Text,             
 98 
 99         /// <summary>
100         /// 对应.NET中的数据类型 String 
101         /// </summary>
102         Nchar,            
103 
104         /// <summary>
105         /// 对应.NET中的数据类型 String 
106         /// </summary>
107         Nvarchar,         
108 
109         /// <summary>
110         /// 对应.NET中的数据类型 String
111         /// </summary>
112         Ntext,             
113  
114         /// <summary>
115         /// 对应.NET中的数据类型 System.Byte[] 
116         /// </summary>
117         Binary,           
118 
119         /// <summary> 
120         /// 对应.NET中的数据类型 System.Byte[] 
121         /// </summary>
122         Varbinary,        
123 
124         /// <summary>
125         /// 对应.NET中的数据类型 System.Byte[] 
126         /// </summary>
127         Image,             
128 
129         /// <summary>
130         /// 对应.NET中的数据类型 System.DateTime 
131         /// </summary>
132         Timestamp,         
133 
134         /// <summary>
135         /// 对应.NET中的数据类型 System.Guid 
136         /// </summary>
137         Uniqueidentifier,  
138 
139         /// <summary>
140         /// 对应.NET中的数据类型 Object 
141         /// </summary>
142         Variant           
143 
144     }
145 }
146 

 

 

以上数据类型都是在程序设计中比较常用的,而且在我们在程序设计的时候,往往都会遇到类型不是完全匹配的时候,在后面的章节中将特殊的讲解一个数据类型与数据类型的不同。在使用CreateParameter() 创建占位符参数的时候,我们也要转化一下类型。之前我们使用SqlParameter 的时候,很多人就是new SqlParameter("@name","qingyuan") 这样对应。就默认为string 类型可以直接转化为sql中的nvarchar,varchar等类型,其实不然。中间还有一个转化过程。

ORM映射框架总结--SQL 语句生成组件ORM映射框架总结--SQL 语句生成组件.net 与SQL 类型的相互转化
 1  /// <summary>
 2         /// 数据库类型的转化
 3         /// </summary>
 4         /// <param name="type">程序中的类型</param>
 5         /// <returns></returns>
 6         private SqlDbType ConvertType(DataType type)
 7         {
 8             SqlDbType sqlType = SqlDbType.BigInt;
 9             switch (type)
10             {
11                 case DataType.Bigint:
12                     sqlType = SqlDbType.BigInt;
13                     break;
14                 case DataType.Binary:
15                     sqlType = SqlDbType.Binary;
16                     break;
17                 case DataType.Bit:
18                     sqlType = SqlDbType.Bit;
19                     break;
20                 case DataType.Char:
21                     sqlType = SqlDbType.Char;
22                     break;
23                 case DataType.Datetime:
24                     sqlType = SqlDbType.DateTime;
25                     break;
26                 case DataType.Decimal:
27                     sqlType = SqlDbType.Decimal;
28                     break;
29                 case DataType.Float:
30                     sqlType = SqlDbType.Float;
31                     break;
32                 case DataType.Image:
33                     sqlType = SqlDbType.Image;
34                     break;
35                 case DataType.Int:
36                     sqlType = SqlDbType.Int;
37                     break;
38                 case DataType.Money:
39                     sqlType = SqlDbType.Money;
40                     break;
41                 case DataType.Nchar:
42                     sqlType = SqlDbType.NChar;
43                     break;
44                 case DataType.Ntext:
45                     sqlType = SqlDbType.NText;
46                     break;
47                 case DataType.Numeric:
48                     sqlType = SqlDbType.Decimal;
49                     break;
50                 case DataType.Nvarchar:
51                     sqlType = SqlDbType.NVarChar;
52                     break;
53                 case DataType.Real:
54                     sqlType = SqlDbType.Float;
55                     break;
56                 case DataType.Smalldatetime:
57                     sqlType = SqlDbType.SmallDateTime;
58                     break;
59                 case DataType.Smallint:
60                     sqlType = SqlDbType.SmallInt;
61                     break;
62                 case DataType.Smallmoney:
63                     sqlType = SqlDbType.SmallMoney;
64                     break;
65                 case DataType.Text:
66                     sqlType = SqlDbType.Text;
67                     break;
68                 case DataType.Timestamp:
69                     sqlType = SqlDbType.Timestamp;
70                     break;
71                 case DataType.Tinyint:
72                     sqlType = SqlDbType.TinyInt;
73                     break;
74                 case DataType.Uniqueidentifier:
75                     sqlType = SqlDbType.UniqueIdentifier;
76                     break;
77                 case DataType.Varbinary:
78                     sqlType = SqlDbType.VarBinary;
79                     break;
80                 case DataType.Varchar:
81                     sqlType = SqlDbType.VarChar;
82                     break;
83                 case DataType.Variant:
84                     sqlType = SqlDbType.Variant;
85                     break;
86 
87             }
88             return sqlType;
89         }
90 

 

 

      所以说做程序不能只看到表面现象,只有深入的去了解他们的原理,知识的使用才能游刃有余。即使千变万化,也是万变不离其宗。

 

     说到这篇文章,我只能说我只写到了很肤浅的一部分。明天要去参加老赵的一个演讲,我想大家都应该这道他的主题是什么了,在文章的结尾我想说,.net 的确有他的奥妙之处。或许我们是觉得.net入门非常简单,这些我承认。说.net 是傻瓜式编程,其实是不是傻瓜式编程这个不是由语言决定的,关键在于你怎么去使用这个东西。C# 的确是一门非常优美的语言,我只能以这种言辞来表达。各种语言编程都有他的好处和坏处,不能从单一角度出发,只有你深入的了解了你就会发现它的美。

(注: ORM涉及内容比较多,后续期待,有兴趣的可以与本人探讨) 

 

 

上一篇:Appendix:附录文件内容记录脚本代码转换过程记录


下一篇:互联网回归理性,程序员何去何从?简单盘点2012~2019技术的热点