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

转帖|其它|编辑:郝浩|2010-12-08 15:19:46.000|阅读 464 次

概述:首先、园子里面之前的很多同仁已经讨论过了ORM相关的框架及其优点和缺点。虽然我本篇讨论的有点晚,但是其毕竟优点大于缺点,本文只是简单的介绍我讨论ORM的目的,及为什么要讨论这个已经被大家讨论的成熟的不能再成熟的东西。

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>

一、开篇

  首先、园子里面之前的很多同仁已经讨论过了ORM相关的框架及其优点和缺点。虽然我本篇讨论的有点晚,但是其毕竟优点大于缺点,本文只是简单的介绍我讨论ORM的目的,及为什么要讨论这个已经被大家讨论的成熟的不能再成熟的东西。

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

   

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

二、本章简介

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

  列举出ORM中的核心模块:

   

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

三、本章内容

   1、摘要。

   2、本章简介。

   3、本章内容。

   4、ORM的应用性分析。

   5、ORM设计分析。

   6、本章总结。

四、ORM的应用性分析

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

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

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

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

   

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

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

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

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

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

   

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

   

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

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

五、ORM设计分析

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

   

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

   实例代码如下:

  
01 public interface IDBAccessor

  02 {

  03 /// <summary>

  04 /// 执行Update,Delete,Insert语句方法

  05 /// </summary>

  06 /// <returns>返回影响的行数</returns>

  07 int Excute();

  08 /// <summary>

  09 /// 执行查询方法

  10 /// </summary>

  11 void Query();

  12 }

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

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

  SQLServer数据库

  01 public class SQLServer : IDBAccessor

  02 {

  03 #region IDBAccessor 成员

  04 private string commandStr = string.Empty;

  05 private static string connectionString = "";

  06 private System.Data.IDbConnection sqlConnection = new System.Data.SqlClient.SqlConnection(connectionString);

  07 public int Excute()

  08 {

  09 if (sqlConnection.State != System.Data.ConnectionState.Open)

  10 sqlConnection.Open();

  11 try

  12 {

  13 using (System.Data.IDbCommand command = sqlConnection.CreateCommand())

  14 {

  15 command.CommandText = commandStr;

  16

  17 return command.ExecuteNonQuery();

  18 }

  19 }

  20 catch(System.Exception)

  21 {

  22 return -1;

  23 }

  24 finally

  25 {

  26

  27 }

  28 }

  29

  30 public void Query()

  31 {

  32 }

  33

  34 #endregion

  35 }

   Oracle数据库

  01 public class Oracle : IDBAccessor

  02 {

  03 #region IDBAccessor 成员

  04 private string commandStr = string.Empty;

  05 private static string connectionString = "";

  06 private System.Data.IDbConnection oraConnection = new System.Data.OracleClient.OracleConnection(connectionString);

  07 public int Excute()

  08 {

  09 if (oraConnection.State != System.Data.ConnectionState.Open)

  10 oraConnection.Open();

  11 try

  12 {

  13 using (System.Data.IDbCommand command = oraConnection.CreateCommand())

  14 {

  15 command.CommandText = commandStr;

  16

  17 return command.ExecuteNonQuery();

  18 }

  19 }

  20 catch (System.Exception)

  21 {

  22 return -1;

  23 }

  24 finally

  25 {

  26

  27 }

  28 }

  29

  30 public void Query()

  31 {

  32 throw new NotImplementedException();

  33 }

  34

  35 #endregion

  36 }

  其他的几个类型的数据库我们就不一一举例说明了,当然我这里面的接口中并没有考虑把数据库连接也定义成接口,让所有的都从这个接口进行继承,因为这个不是 本章讨论的重点,本章只是简单的分析与设计如何实现通用把数据层访问。

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

  

  我们比较常见的方式目前就这样的2种方式,第一种方式想必大家都比较了解的,无论是JAVA中的Hibernate还是.NET中的Nhibernate都是这样的方式,以XML文件的方式把数据库中的表列属性与实体的属性一一对应。第二种方式则是在类文件中硬编码书写数据库列与实体之间的映射关系。

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

  

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

  

  当然以上的2种形式各有优缺点,我们已经讲述了XML配置文件中现有的开源框架中采用这种形式的框架有Nhibernate。而采用类文件映射的框架其实有很多,但是他们的思想相对来说都是一样的。不管是XML文件形式,还是类文件形式,他们的主要观点都是实现如何把实体的属性与数据库表字段的对应,这个才是核心的内容.下面我们给出一种简单的思路去完成这样的映射,当然我们这里是以类文件形式给出示例。

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

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

  具体相应代码如下:

  001 /// <summary>

  002 /// Model中的字段属性特性

  003 /// </summary>

  004 [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]

  005 public class PropertyAttribute : Attribute

  006 {

  007 private string dbColumnName;

  008 private bool isPrimary;

  009 private DbType dbType;

  010 private object defaultValue;

  011 private bool isIdentify;

  012 private int length;

  013

  014 public string DbColumnName

  015 {

  016 get

  017 {

  018 return this.dbColumnName;

  019 }

  020 set

  021 {

  022 this.dbColumnName = value;

  023 }

  024 }

  025

  026 public bool IsPrimary

  027 {

  028 get

  029 {

  030 return this.isPrimary;

  031 }

  032 set

  033 {

  034 this.isPrimary = value;

  035 }

  036 }

  037

  038 public bool IsIdentify

  039 {

  040 get

  041 {

  042 return this.isIdentify;

  043 }

  044 set

  045 {

  046 this.isIdentify = value;

  047 }

  048 }

  049

  050 public DbType DbType

  051 {

  052 get

  053 {

  054 return this.dbType;

  055 }

  056 set

  057 {

  058 this.dbType = value;

  059 }

  060 }

  061

  062 public object DefaultValue

  063 {

  064 get

  065 {

  066 return this.defaultValue;

  067 }

  068 set

  069 {

  070 this.defaultValue = value;

  071 }

  072 }

  073

  074 public int DbLength

  075 {

  076 get

  077 {

  078 return this.length;

  079 }

  080 set

  081 {

  082 this.length = value;

  083 }

  084 }

  085 public PropertyAttribute(string dbName, bool isPrimery, DbType type,object dValue)

  086 {

  087 this.dbColumnName = dbName;

  088 this.isPrimary = isPrimery;

  089 this.dbType = type;

  090 this.defaultValue = this.GetDefaultValue();

  091 }

  092

  093 private object GetDefaultValue()

  094 {

  095 return new object();

  096 }

  097

  098 public PropertyAttribute(string dbName)

  099 {

  100 this.dbColumnName = dbName;

  101 this.isPrimary = false;

  102 this.dbType = DbType.String;

  103 this.defaultValue = this.GetDefaultValue();

  104 }

  105

  106 public PropertyAttribute(string dbName,bool isPrimery)

  107 {

  108 this.dbColumnName = dbName;

  109 this.isPrimary = isPrimery;

  110 this.dbType = DbType.String;

  111 this.defaultValue = this.GetDefaultValue();

  112 }

  113

  114 public PropertyAttribute(string dbName, bool isPrimery, DbType type)

  115 {

  116 this.dbColumnName = dbName;

  117 this.isPrimary = isPrimery;

  118 this.dbType = type;

  119 this.defaultValue = null;

  120 }

  121 }

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

  01 /// <summary>

  02 /// 基于表的自定义特性类

  03 /// </summary>

  04 [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]

  05 public class TableAttribute : Attribute

  06 {

  07 private string dbTableName;

  08 public TableAttribute(string dbName)

  09 {

  10 this.dbTableName = dbName;

  11 }

  12

  13 public string TableName

  14 {

  15 get

  16 {

  17 return this.dbTableName;

  18 }

  19 set

  20 {

  21 this.dbTableName = value;

  22 }

  23 }

  24 }

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

  01 /// <summary>

  02 /// 管理员账户ID

  03 /// </summary>

  04 [PropertyAttribute("",false,System.Data.DbType.Int32,0)]

  05 public int AdminId

  06 {

  07 set

  08 {

  09 _adminid = value;

  10 }

  11 get

  12 {

  13 return _adminid;

  14 }

  15 }

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

  1 [TableAttribute("es_memberaccount")]

  2 public class Account

  3 {

  4 public Account()

  5 {

  6 }

  7 }

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

  01 /// <summary>

  02 /// 返回Model对应的数据库表名

  03 /// </summary>

  04 /// <typeparam name="T"></typeparam>

  05 /// <param name="model"></param>

  06 /// <returns></returns>

  07 public string DbTableName<T>(T model)

  08 {

  09 string dbName = string.Empty;

  10 DPM.Common.TableAttribute attr = null;

  11

  12 object[] attributes = model.GetType().GetCustomAttributes(typeof(DPM.Common.TableAttribute), true);

  13

  14 if (attributes.Length > 0)

  15 {

  16 attr = (DPM.Common.TableAttribute)attributes[0];

  17 }

  18

  19 if (attr != null)

  20 dbName = attr.TableName;

  21

  22 return dbName;

  23 }

  24

  25 /// <summary>

  26 /// 返回数据库表中的所有数据列

  27 /// </summary>

  28 /// <typeparam name="T"></typeparam>

  29 /// <param name="model"></param>

  30 /// <returns></returns>

  31 public string InitDbColumns<T>(T model)

  32 {

  33 StringBuilder commandBuilder = new StringBuilder();

  34

  35 DPM.Common.PropertyAttribute attr = null;

  36

  37 foreach (PropertyInfo property in model.GetType().GetProperties())

  38 {

  39 object[] attributes = property.GetCustomAttributes(typeof(DPM.Common.PropertyAttribute), true);

  40 if (attributes.Length > 0)

  41 {

  42 attr = (DPM.Common.PropertyAttribute)attributes[0];

  43 }

  44

  45 commandBuilder.Append(attr.DbColumnName+",");

  46 }

  47

  48 return commandBuilder.ToString().Substring(0,commandBuilder.ToString().Length-1);

  49 }

六、本章总结

  本章简单讲述了ORM实现的基本思路分析及ORM框架使用的优缺点及在项目中如何合理的分析与应用。下面我们来简单总结下本章讲解的内容。本章主要讲述了ORM的优点:减少工作流,复用性高,开发速度快,更关注业务方面的开发,将DDL操作中除了联合查询实现起来比较复杂外,其他的基本上都能正常的处理。缺点:一对多或者多对多的关联关系无法很好的满足需求外,还有就是性能上会有一定的影响。在项目中应根据项目的业务需求来决定是否在项目中使用ORM框架来解决问题。

   


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com

文章转载自:网络转载

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP