Step by Step-构建自己的ORM系列-开篇

简介:

 

一、开篇

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

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

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

       image

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

二、本章简介

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

列举出ORM中的核心模块:

       image

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

三、本章内容

       1、摘要。

       2、本章简介。

       3、本章内容。

       4、ORM的应用性分析。

       5、ORM设计分析。

       6、本章总结。

       7、系列进度。

       8、下篇预告。

四、ORM的应用性分析

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

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

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

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

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

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

       image

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

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

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

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

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

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

       image

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

        image

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

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

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

五、ORM设计分析

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

        image

        上图大概画出了比较常用的几类数据库,通过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
}

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

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

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

       image

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

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

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

       image

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

      image

      当然以上的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

相关文章
|
7月前
|
机器学习/深度学习 索引
yolov5--loss.py --v5.0版本-最新代码详细解释-2021-7-1更新
yolov5--loss.py --v5.0版本-最新代码详细解释-2021-7-1更新
317 0
|
存储 SQL 数据库
Step By Step 一步一步写网站[1] —— 填加数据
填加数据是一个项目必不可少的部分,也是一个基础操作,使用也是最频繁的。那么您是怎么实现添加数据的呢?添加数据可以分为几种情况。1、单表添加,不需要事务。最简单最常见2、多表(主从表)添加,不需要事务。3、多表(主从表)添加,需要事务。
986 0
|
SQL 存储 数据库
Step By Step 一步一步写网站[1] —— 填加数据(二)
(这个是我认真思考并精心写的,能不能算作高质量我就不敢说了)建议先看一下第一篇:http://www.cnblogs.com/jyk/archive/2007/03/23/685075.html 上一篇写了我常用的方法,这里想说一下我猜想的OOD的方法,之所以用猜想,是因为我没有用过OOD的方法,我也不知道我这么写对不对,所以用了“猜想”二字。
1110 0
|
Web App开发 前端开发 索引
原生开发移动web单页面(step by step)8——History对象
增加了页面切换效果后,再配合手势以及按键,让单页面app更像一个原生app, 然而却引来了一个新的问题, 通过浏览器自带的前进后退无法有选择的采取动画效果。
1349 0