博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Step by Step-构建自己的ORM系列-开篇
阅读量:6692 次
发布时间:2019-06-25

本文共 8302 字,大约阅读时间需要 27 分钟。

 

一、开篇

       首先、园子里面之前的很多同仁已经讨论过了ORM相关的框架及其优点和缺点。虽然我本篇讨论的有点晚,但是其毕竟优点大于缺点,本文只是简单的介绍我讨

论ORM的目的,及为什么要讨论这个已经被大家讨论的成熟的不能再成熟的东西。

       我们先来看看ORM的优缺点:

       

       本篇将详细分析为什么我们要使用ORM,及ORM前篇的分析建模。

二、本章简介

       本篇主要是分析ORM框架的优缺点及项目中的取舍,找到突破点,并且通过图形化的方式分析ORM应该具备的基本功能及如何构建ORM中的核心模块。这里面简单

列举出ORM中的核心模块:

       

       下面将分别讲解和分析实现方案。

三、本章内容

       1、摘要。

       2、本章简介。

       3、本章内容。

       4、ORM的应用性分析。

       5、ORM设计分析。

       6、本章总结。

       7、系列进度。

       8、下篇预告。

四、ORM的应用性分析

       首先、在软件开发中我们都知道OO面向对象的思想对我们现有的软件开发意义,我们可以把软件开发的过程理解为将现实社会的抽象过程。面向对象的思想把现

实世界抽象为万物皆为对象,通过对象之间的交互完成所有的活动。OO出现之前的软件开发都是面向过程的开发思想。面向过程关系的是过程而不是对象。在某个动作

过程中的步骤,通过一系列的函数来解决问题。

       面向对象则把一切事物看作对象,而过程就是对象之间的交互或是对象内部的活动。

       我们知道目前流行的数据库都是关系型数据库,二维的数据库结构。我们如何将某个对象与这个实体对应起来呢?这就成了我们更关心的问题,这时候ORM思想

的出现解决了这样的问题。

       

       上图反映了实体对象与数据库表的关系,一个实体对象对应数据库表中的一个行记录。而通过DDL操作中的查询方法,来将数据库表中的行纪录映射到多个实体对

象中。而通过ORM提供的DDL操作方法,将实体对象的数据持久化到对应的数据库表中。

       另外一个需要注意的问题就是当实体中的属性添加或减少时或是数据库表中的结构发生变化时,如何做到实体中的属性与数据库表中的列一一对应这是个每个ORM

头疼的问题,因为ORM无法实现自动的同步这样的变化。当然目前的大名鼎鼎的Nhibernate在这方面也是处理的比较灵活,这是必须肯定的。当然在这个系列中我们也

会详细的讲解实现的思路与方案,如何处理实体与数据库表结构发生变化时的同步问题,当然这和采用的ORM的实现方式有关。

       ORM思想给我提供了如下的方便:

       

        当然ORM框架也不是万能的,有优点的必然存在这一定的缺点,我们来看看ORM的不足:

        

        通过上面的分析我们简单的了解了ORM的优缺点,那么如何在项目中应用它呢,我们在使用某个技术时肯定是扬长避短,所以ORM也是一样的道理,如果我们在项目中有大量的DDL操作语句,并且对业务逻辑之间的多实体间的关联关系不是特别的紧密时,那么用ORM技术就会比较好。

        如果在项目中多表的关联查询比较多,并且表之间的逻辑关系比较复杂时就不推荐用ORM来处理。不但会提高项目的复杂度,维护起来代价也比较大。例如像统

计分析系统。用ORM来实现就比较麻烦。

五、ORM设计分析

        首先我们来看看数据库访问的通用组件模型:

        

        上图大概画出了比较常用的几类数据库,通过ORM中的数据库访问组件来实现数据库的访问。当然我们这里通过定义数据库访问统一接口的形式,让所有的数据

库访问类都默认继承实现这个接口。

        实例代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
public  
interface 
IDBAccessor
 
{
     
/// <summary>
     
/// 执行Update,Delete,Insert语句方法
     
/// </summary>
     
/// <returns>返回影响的行数</returns>
     
int 
Excute();
     
/// <summary>
     
/// 执行查询方法
     
/// </summary>
     
void 
Query();
 
}

     接口中只是定义了简单的DDL语言中的四个基本的操作。

     下面看每个不同数据库的实现方法。

     SQLServer数据库

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public 
class 
SQLServer : IDBAccessor
{
    
#region IDBAccessor 成员
    
private 
string 
commandStr = 
string
.Empty;
    
private 
static 
string 
connectionString = 
""
;
    
private 
System.Data.IDbConnection sqlConnection = 
new 
System.Data.SqlClient.SqlConnection(connectionString);
    
public 
int 
Excute()
    
{
        
if 
(sqlConnection.State != System.Data.ConnectionState.Open)
            
sqlConnection.Open();
        
try
        
{
            
using 
(System.Data.IDbCommand command = sqlConnection.CreateCommand())
            
{
                
command.CommandText = commandStr;
 
                
return 
command.ExecuteNonQuery();
            
}
        
}
        
catch
(System.Exception)
        
{
            
return 
-1;
        
}
        
finally
        
{
 
        
}
    
}
 
    
public 
void 
Query()
    
{
    
}
 
    
#endregion
}

  Oracle数据库

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public 
class 
Oracle : IDBAccessor
{
    
#region IDBAccessor 成员
    
private 
string 
commandStr = 
string
.Empty;
    
private 
static 
string 
connectionString = 
""
;
    
private 
System.Data.IDbConnection oraConnection = 
new 
System.Data.OracleClient.OracleConnection(connectionString);
    
public 
int 
Excute()
    
{
        
if 
(oraConnection.State != System.Data.ConnectionState.Open)
            
oraConnection.Open();
        
try
        
{
            
using 
(System.Data.IDbCommand command = oraConnection.CreateCommand())
            
{
                
command.CommandText = commandStr;
 
                
return 
command.ExecuteNonQuery();
            
}
        
}
        
catch 
(System.Exception)
        
{
            
return 
-1;
        
}
        
finally
        
{
 
        
}
    
}
 
    
public 
void 
Query()
    
{
        
throw 
new 
NotImplementedException();
    
}
 
    
#endregion
}

   其他的几个类型的数据库我们就不一一举例说明了,当然我这里面的接口中并没有考虑把数据库连接也定义成接口,让所有的都从这个接口进行继承,因为这个不是

本章讨论的重点,本章只是简单的分析与设计如何实现通用把数据层访问。

      下面我们来说说对象关系映射的实现。

       

       我们比较常见的方式目前就这样的2种方式,第一种方式想必大家都比较了解的,无论是JAVA中的Hibernate还是.NET中的Nhibernate都是这样的方式,以XML文

件的方式把数据库中的表列属性与实体的属性一一对应。第二种方式则是在类文件中硬编码书写数据库列与实体之间的映射关系。

       下面我们来分析下这二种方式的利弊:

       

       以上大概描述了各自的有点,下面再阐述下各自的缺点。

      

      当然以上的2种形式各有优缺点,我们已经讲述了XML配置文件中现有的开源框架中采用这种形式的框架有Nhibernate。而采用类文件映射的框架其实有很多,但

是他们的思想相对来说都是一样的。不管是XML文件形式,还是类文件形式,他们的主要观点都是实现如何把实体的属性与数据库表字段的对应,这个才是核心的内容。

下面我们给出一种简单的思路去完成这样的映射,当然我们这里是以类文件形式给出示例。

       其实博客园的很多人都写过类文件映射的实例。我这里当然也只是抛砖引玉,不足之处在所难免,还请大家多多提出意见。

       我给出的方式是通过特性(Attribute)+反射(Rflection)的思想来实现ORM映射。

       具体相应代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/// <summary>
/// Model中的字段属性特性
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = 
false
)]
public 
class 
PropertyAttribute : Attribute
{
    
private 
string 
dbColumnName;
    
private 
bool 
isPrimary;
    
private 
DbType dbType;
    
private 
object 
defaultValue;
    
private 
bool 
isIdentify;
    
private 
int 
length;
 
    
public 
string 
DbColumnName
    
{
        
get
        
{
            
return 
this
.dbColumnName;
        
}
        
set
        
{
            
this
.dbColumnName = value;
        
}
    
}
 
    
public 
bool 
IsPrimary
    
{
        
get
        
{
            
return 
this
.isPrimary;
        
}
        
set
        
{
            
this
.isPrimary = value;
        
}
    
}
 
    
public 
bool 
IsIdentify
    
{
        
get
        
{
            
return 
this
.isIdentify;
        
}
        
set
        
{
            
this
.isIdentify = value;
        
}
    
}
 
    
public 
DbType DbType
    
{
        
get
        
{
            
return 
this
.dbType;
        
}
        
set
        
{
            
this
.dbType = value;
        
}
    
}
 
    
public 
object 
DefaultValue
    
{
        
get
        
{
            
return 
this
.defaultValue;
        
}
        
set
        
{
            
this
.defaultValue = value;
        
}
    
}
 
    
public 
int 
DbLength
    
{
        
get
        
{
            
return 
this
.length;
        
}
        
set
        
{
            
this
.length = value;
        
}
    
}
    
public 
PropertyAttribute(
string 
dbName, 
bool 
isPrimery, DbType type,
object 
dValue)
    
{
        
this
.dbColumnName = dbName;
        
this
.isPrimary = isPrimery;
        
this
.dbType = type;
        
this
.defaultValue = 
this
.GetDefaultValue();
    
}
 
    
private 
object 
GetDefaultValue()
    
{
        
return 
new 
object
();
    
}
 
    
public 
PropertyAttribute(
string 
dbName)
    
{
        
this
.dbColumnName = dbName;
        
this
.isPrimary = 
false
;
        
this
.dbType = DbType.String;
        
this
.defaultValue = 
this
.GetDefaultValue();
    
}
 
    
public 
PropertyAttribute(
string 
dbName,
bool 
isPrimery)
    
{
        
this
.dbColumnName = dbName;
        
this
.isPrimary = isPrimery;
        
this
.dbType = DbType.String;
        
this
.defaultValue = 
this
.GetDefaultValue();
    
}
 
    
public 
PropertyAttribute(
string 
dbName, 
bool 
isPrimery, DbType type)
    
{
        
this
.dbColumnName = dbName;
        
this
.isPrimary = isPrimery;
        
this
.dbType = type;
        
this
.defaultValue = 
null
;
    
}
}

   上面给出的是字段属性上定义的特性,我们来看看表的特性:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
 
/// 基于表的自定义特性类
 
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = 
false
)]
public 
class 
TableAttribute : Attribute
 
{
     
private 
string 
dbTableName;
     
public 
TableAttribute(
string 
dbName)
     
{
         
this
.dbTableName = dbName;
     
}
 
     
public 
string 
TableName
     
{
         
get
         
{
             
return 
this
.dbTableName;
         
}
         
set
         
{
             
this
.dbTableName = value;
         
}
     
}
 
}

      在实体层的具体使用如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// <summary>
/// 管理员账户ID
/// </summary>
[PropertyAttribute(
""
,
false
,System.Data.DbType.Int32,0)]
public 
int 
AdminId
{
    
set
    
{
        
_adminid = value;
    
}
    
get
    
{
        
return 
_adminid;
    
}
}

       基于表上的特性如下使用:

 

1
2
3
4
5
6
7
[TableAttribute(
"es_memberaccount"
)]
public 
class 
Account
{
    
public 
Account()
    
{
    
}
 
}

 

      下面看看如何在生成SQL语句层中的处理方法:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/// <summary>
/// 返回Model对应的数据库表名
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="model"></param>
/// <returns></returns>
public 
string 
DbTableName<T>(T model)
{
    
string 
dbName = 
string
.Empty;
    
DPM.Common.TableAttribute attr = 
null
;
 
    
object
[] attributes = model.GetType().GetCustomAttributes(
typeof
(DPM.Common.TableAttribute), 
true
);
 
    
if 
(attributes.Length > 0)
    
{
        
attr = (DPM.Common.TableAttribute)attributes[0];
    
}
 
    
if 
(attr != 
null
)
        
dbName = attr.TableName;
 
    
return 
dbName;
}
 
/// <summary>
/// 返回数据库表中的所有数据列
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="model"></param>
/// <returns></returns>
public 
string 
InitDbColumns<T>(T model)
{
    
StringBuilder commandBuilder = 
new 
StringBuilder();
 
    
DPM.Common.PropertyAttribute attr = 
null
;
 
    
foreach 
(PropertyInfo property 
in 
model.GetType().GetProperties())
    
{
        
object
[] attributes = property.GetCustomAttributes(
typeof
(DPM.Common.PropertyAttribute), 
true
);
        
if 
(attributes.Length > 0)
        
{
            
attr = (DPM.Common.PropertyAttribute)attributes[0];
        
}
 
        
commandBuilder.Append(attr.DbColumnName+”,”);
    
}
 
    
return 
commandBuilder.ToString().Substring(0,commandBuilder.ToString().Length-1);
}

六、本章总结

      本章简单讲述了ORM实现的基本思路分析及ORM框架使用的优缺点及在项目中如何合理的分析与应用。下面我们来简单总结下本章讲解的内容。

      本章主要讲述了ORM的优点:减少工作流,复用性高,开发速度快,更关注业务方面的开发,将DDL操作中除了联合查询实现起来比较复杂外,其他的基本上都

能正常的处理。缺点:一对多或者多对多的关联关系无法很好的满足需求外,还有就是性能上会有一定的影响。在项目中应根据项目的业务需求来决定是否在项目中使

用ORM框架来解决问题。

七、下篇预告

        下篇我们将讲解如何实现通用的数据访问层,将会详细的介绍如何设计出通用的数据访问层,并且采用设计模中的2个原则:低耦合、高内聚等一些设计规范和原

则。欢迎大家拍砖和提出好的意见和建议。

本文转自 hot的fans  51CTO博客,原文链接:http://blog.51cto.com/2435232/590959

转载地址:http://rnjoo.baihongyu.com/

你可能感兴趣的文章
Angular service 详解
查看>>
百度研发面经
查看>>
深度解析 Go 语言中「切片」的三种特殊状态
查看>>
ES6 - 函数扩展
查看>>
Linux中apt与apt-get命令的区别与解释(转)
查看>>
原生js 类名操作 增加 删除
查看>>
iOS 中多音频处理
查看>>
java.lang.IllegalStateException: aidl is missing
查看>>
js的数组和对象的多种"复制"和"清空", 以及区分JS数组和对象的方法
查看>>
爬虫提交form表单中含有(unable to decode value)解决方法
查看>>
Vagrant (二) - 日常操作
查看>>
PHP常用180函数总结
查看>>
React 中的事件处理
查看>>
.NET环境大规模使用OpenTracing
查看>>
js 快速排序
查看>>
Sentry的使用
查看>>
如何在微服务架构中对资源(前端页面+后端接口)进行权限控制
查看>>
原生 js 实现面对对象版瀑布流
查看>>
逝去的2018年,年度总结
查看>>
链客区块链技术面试题目专题(二)
查看>>