天天看點

Step by Step-建構自己的ORM系列-開篇

       首先、園子裡面之前的很多同仁已經讨論過了ORM相關的架構及其優點和缺點。雖然我本篇讨論的有點晚,但是其畢竟優點大于缺點,本文隻是簡單的介紹我讨

論ORM的目的,及為什麼要讨論這個已經被大家讨論的成熟的不能再成熟的東西。

       我們先來看看ORM的優缺點:

       本篇将詳細分析為什麼我們要使用ORM,及ORM前篇的分析模組化。

       本篇主要是分析ORM架構的優缺點及項目中的取舍,找到突破點,并且通過圖形化的方式分析ORM應該具備的基本功能及如何建構ORM中的核心子產品。這裡面簡單

列舉出ORM中的核心子產品:

       下面将分别講解和分析實作方案。

       1、摘要。

       2、本章簡介。

       3、本章内容。

       4、ORM的應用性分析。

       5、ORM設計分析。

       6、本章總結。

       7、系列進度。

       8、下篇預告。

       首先、在軟體開發中我們都知道OO面向對象的思想對我們現有的軟體開發意義,我們可以把軟體開發的過程了解為将現實社會的抽象過程。面向對象的思想把現

實世界抽象為萬物皆為對象,通過對象之間的互動完成所有的活動。OO出現之前的軟體開發都是面向過程的開發思想。面向過程關系的是過程而不是對象。在某個動作

過程中的步驟,通過一系列的函數來解決問題。

       面向對象則把一切事物看作對象,而過程就是對象之間的互動或是對象内部的活動。

       我們知道目前流行的資料庫都是關系型資料庫,二維的資料庫結構。我們如何将某個對象與這個實體對應起來呢?這就成了我們更關心的問題,這時候ORM思想

的出現解決了這樣的問題。

       上圖反映了實體對象與資料庫表的關系,一個實體對象對應資料庫表中的一個行記錄。而通過DDL操作中的查詢方法,來将資料庫表中的行紀錄映射到多個實體對

象中。而通過ORM提供的DDL操作方法,将實體對象的資料持久化到對應的資料庫表中。

       另外一個需要注意的問題就是當實體中的屬性添加或減少時或是資料庫表中的結構發生變化時,如何做到實體中的屬性與資料庫表中的列一一對應這是個每個ORM

頭疼的問題,因為ORM無法實作自動的同步這樣的變化。當然目前的大名鼎鼎的Nhibernate在這方面也是處理的比較靈活,這是必須肯定的。當然在這個系列中我們也

會詳細的講解實作的思路與方案,如何處理實體與資料庫表結構發生變化時的同步問題,當然這和采用的ORM的實作方式有關。

       ORM思想給我提供了如下的友善:

        當然ORM架構也不是萬能的,有優點的必然存在這一定的缺點,我們來看看ORM的不足:

        通過上面的分析我們簡單的了解了ORM的優缺點,那麼如何在項目中應用它呢,我們在使用某個技術時肯定是揚長避短,是以ORM也是一樣的道理,如果我們在項目中有大量的DDL操作語句,并且對業務邏輯之間的多實體間的關聯關系不是特别的緊密時,那麼用ORM技術就會比較好。

        如果在項目中多表的關聯查詢比較多,并且表之間的邏輯關系比較複雜時就不推薦用ORM來處理。不但會提高項目的複雜度,維護起來代價也比較大。例如像統

計分析系統。用ORM來實作就比較麻煩。

        首先我們來看看資料庫通路的通用元件模型:

        上圖大概畫出了比較常用的幾類資料庫,通過ORM中的資料庫通路元件來實作資料庫的通路。當然我們這裡通過定義資料庫通路統一接口的形式,讓所有的資料

庫通路類都預設繼承實作這個接口。

        執行個體代碼如下:

<a href="http://blog.51cto.com/2435232/590959#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

<code>public</code>  <code>interface</code> <code>IDBAccessor</code>

<code> </code><code>{</code>

<code>     </code><code>/// &lt;summary&gt;</code>

<code>     </code><code>/// 執行Update,Delete,Insert語句方法</code>

<code>     </code><code>/// &lt;/summary&gt;</code>

<code>     </code><code>/// &lt;returns&gt;傳回影響的行數&lt;/returns&gt;</code>

<code>     </code><code>int</code> <code>Excute();</code>

<code>     </code><code>/// 執行查詢方法</code>

<code>     </code><code>void</code> <code>Query();</code>

<code> </code><code>}</code>

     接口中隻是定義了簡單的DDL語言中的四個基本的操作。

     下面看每個不同資料庫的實作方法。

     SQLServer資料庫

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

<code>public</code> <code>class</code> <code>SQLServer : IDBAccessor</code>

<code>{</code>

<code>    </code><code>#region IDBAccessor 成員</code>

<code>    </code><code>private</code> <code>string</code> <code>commandStr = </code><code>string</code><code>.Empty;</code>

<code>    </code><code>private</code> <code>static</code> <code>string</code> <code>connectionString = </code><code>""</code><code>;</code>

<code>    </code><code>private</code> <code>System.Data.IDbConnection sqlConnection = </code><code>new</code> <code>System.Data.SqlClient.SqlConnection(connectionString);</code>

<code>    </code><code>public</code> <code>int</code> <code>Excute()</code>

<code>    </code><code>{</code>

<code>        </code><code>if</code> <code>(sqlConnection.State != System.Data.ConnectionState.Open)</code>

<code>            </code><code>sqlConnection.Open();</code>

<code>        </code><code>try</code>

<code>        </code><code>{</code>

<code>            </code><code>using</code> <code>(System.Data.IDbCommand command = sqlConnection.CreateCommand())</code>

<code>            </code><code>{</code>

<code>                </code><code>command.CommandText = commandStr;</code>

<code>                </code><code>return</code> <code>command.ExecuteNonQuery();</code>

<code>            </code><code>}</code>

<code>        </code><code>}</code>

<code>        </code><code>catch</code><code>(System.Exception)</code>

<code>            </code><code>return</code> <code>-1;</code>

<code>        </code><code>finally</code>

<code>    </code><code>}</code>

<code>    </code><code>public</code> <code>void</code> <code>Query()</code>

<code>    </code><code>#endregion</code>

<code>}</code>

  Oracle資料庫

36

<code>public</code> <code>class</code> <code>Oracle : IDBAccessor</code>

<code>    </code><code>private</code> <code>System.Data.IDbConnection oraConnection = </code><code>new</code> <code>System.Data.OracleClient.OracleConnection(connectionString);</code>

<code>        </code><code>if</code> <code>(oraConnection.State != System.Data.ConnectionState.Open)</code>

<code>            </code><code>oraConnection.Open();</code>

<code>            </code><code>using</code> <code>(System.Data.IDbCommand command = oraConnection.CreateCommand())</code>

<code>        </code><code>catch</code> <code>(System.Exception)</code>

<code>        </code><code>throw</code> <code>new</code> <code>NotImplementedException();</code>

   其他的幾個類型的資料庫我們就不一一舉例說明了,當然我這裡面的接口中并沒有考慮把資料庫連接配接也定義成接口,讓所有的都從這個接口進行繼承,因為這個不是

本章讨論的重點,本章隻是簡單的分析與設計如何實作通用把資料層通路。

      下面我們來說說對象關系映射的實作。

       我們比較常見的方式目前就這樣的2種方式,第一種方式想必大家都比較了解的,無論是JAVA中的Hibernate還是.NET中的Nhibernate都是這樣的方式,以XML文

件的方式把資料庫中的表列屬性與實體的屬性一一對應。第二種方式則是在類檔案中寫死書寫資料庫列與實體之間的映射關系。

       下面我們來分析下這二種方式的利弊:

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

      當然以上的2種形式各有優缺點,我們已經講述了XML配置檔案中現有的開源架構中采用這種形式的架構有Nhibernate。而采用類檔案映射的架構其實有很多,但

是他們的思想相對來說都是一樣的。不管是XML檔案形式,還是類檔案形式,他們的主要觀點都是實作如何把實體的屬性與資料庫表字段的對應,這個才是核心的内容。

下面我們給出一種簡單的思路去完成這樣的映射,當然我們這裡是以類檔案形式給出示例。

       其實部落格園的很多人都寫過類檔案映射的執行個體。我這裡當然也隻是抛磚引玉,不足之處在所難免,還請大家多多提出意見。

       我給出的方式是通過特性(Attribute)+反射(Rflection)的思想來實作ORM映射。

       具體相應代碼如下:

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

<code>/// &lt;summary&gt;</code>

<code>/// Model中的字段屬性特性</code>

<code>/// &lt;/summary&gt;</code>

<code>[AttributeUsage(AttributeTargets.All, AllowMultiple = </code><code>false</code><code>)]</code>

<code>public</code> <code>class</code> <code>PropertyAttribute : Attribute</code>

<code>    </code><code>private</code> <code>string</code> <code>dbColumnName;</code>

<code>    </code><code>private</code> <code>bool</code> <code>isPrimary;</code>

<code>    </code><code>private</code> <code>DbType dbType;</code>

<code>    </code><code>private</code> <code>object</code> <code>defaultValue;</code>

<code>    </code><code>private</code> <code>bool</code> <code>isIdentify;</code>

<code>    </code><code>private</code> <code>int</code> <code>length;</code>

<code>    </code><code>public</code> <code>string</code> <code>DbColumnName</code>

<code>        </code><code>get</code>

<code>            </code><code>return</code> <code>this</code><code>.dbColumnName;</code>

<code>        </code><code>set</code>

<code>            </code><code>this</code><code>.dbColumnName = value;</code>

<code>    </code><code>public</code> <code>bool</code> <code>IsPrimary</code>

<code>            </code><code>return</code> <code>this</code><code>.isPrimary;</code>

<code>            </code><code>this</code><code>.isPrimary = value;</code>

<code>    </code><code>public</code> <code>bool</code> <code>IsIdentify</code>

<code>            </code><code>return</code> <code>this</code><code>.isIdentify;</code>

<code>            </code><code>this</code><code>.isIdentify = value;</code>

<code>    </code><code>public</code> <code>DbType DbType</code>

<code>            </code><code>return</code> <code>this</code><code>.dbType;</code>

<code>            </code><code>this</code><code>.dbType = value;</code>

<code>    </code><code>public</code> <code>object</code> <code>DefaultValue</code>

<code>            </code><code>return</code> <code>this</code><code>.defaultValue;</code>

<code>            </code><code>this</code><code>.defaultValue = value;</code>

<code>    </code><code>public</code> <code>int</code> <code>DbLength</code>

<code>            </code><code>return</code> <code>this</code><code>.length;</code>

<code>            </code><code>this</code><code>.length = value;</code>

<code>    </code><code>public</code> <code>PropertyAttribute(</code><code>string</code> <code>dbName, </code><code>bool</code> <code>isPrimery, DbType type,</code><code>object</code> <code>dValue)</code>

<code>        </code><code>this</code><code>.dbColumnName = dbName;</code>

<code>        </code><code>this</code><code>.isPrimary = isPrimery;</code>

<code>        </code><code>this</code><code>.dbType = type;</code>

<code>        </code><code>this</code><code>.defaultValue = </code><code>this</code><code>.GetDefaultValue();</code>

<code>    </code><code>private</code> <code>object</code> <code>GetDefaultValue()</code>

<code>        </code><code>return</code> <code>new</code> <code>object</code><code>();</code>

<code>    </code><code>public</code> <code>PropertyAttribute(</code><code>string</code> <code>dbName)</code>

<code>        </code><code>this</code><code>.isPrimary = </code><code>false</code><code>;</code>

<code>        </code><code>this</code><code>.dbType = DbType.String;</code>

<code>    </code><code>public</code> <code>PropertyAttribute(</code><code>string</code> <code>dbName,</code><code>bool</code> <code>isPrimery)</code>

<code>    </code><code>public</code> <code>PropertyAttribute(</code><code>string</code> <code>dbName, </code><code>bool</code> <code>isPrimery, DbType type)</code>

<code>        </code><code>this</code><code>.defaultValue = </code><code>null</code><code>;</code>

   上面給出的是字段屬性上定義的特性,我們來看看表的特性:

<code> </code><code>/// 基于表的自定義特性類</code>

<code> </code><code>/// &lt;/summary&gt;</code>

<code>public</code> <code>class</code> <code>TableAttribute : Attribute</code>

<code>     </code><code>private</code> <code>string</code> <code>dbTableName;</code>

<code>     </code><code>public</code> <code>TableAttribute(</code><code>string</code> <code>dbName)</code>

<code>     </code><code>{</code>

<code>         </code><code>this</code><code>.dbTableName = dbName;</code>

<code>     </code><code>}</code>

<code>     </code><code>public</code> <code>string</code> <code>TableName</code>

<code>         </code><code>get</code>

<code>         </code><code>{</code>

<code>             </code><code>return</code> <code>this</code><code>.dbTableName;</code>

<code>         </code><code>}</code>

<code>         </code><code>set</code>

<code>             </code><code>this</code><code>.dbTableName = value;</code>

      在實體層的具體使用如下:

<code>/// 管理者賬戶ID</code>

<code>[PropertyAttribute(</code><code>""</code><code>,</code><code>false</code><code>,System.Data.DbType.Int32,0)]</code>

<code>public</code> <code>int</code> <code>AdminId</code>

<code>    </code><code>set</code>

<code>        </code><code>_adminid = value;</code>

<code>    </code><code>get</code>

<code>        </code><code>return</code> <code>_adminid;</code>

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

<code>[TableAttribute(</code><code>"es_memberaccount"</code><code>)]</code>

<code>public</code> <code>class</code> <code>Account</code>

<code>    </code><code>public</code> <code>Account()</code>

      下面看看如何在生成SQL語句層中的處理方法:

<code>/// 傳回Model對應的資料庫表名</code>

<code>/// &lt;typeparam name="T"&gt;&lt;/typeparam&gt;</code>

<code>/// &lt;param name="model"&gt;&lt;/param&gt;</code>

<code>/// &lt;returns&gt;&lt;/returns&gt;</code>

<code>public</code> <code>string</code> <code>DbTableName&lt;T&gt;(T model)</code>

<code>    </code><code>string</code> <code>dbName = </code><code>string</code><code>.Empty;</code>

<code>    </code><code>DPM.Common.TableAttribute attr = </code><code>null</code><code>;</code>

<code>    </code><code>object</code><code>[] attributes = model.GetType().GetCustomAttributes(</code><code>typeof</code><code>(DPM.Common.TableAttribute), </code><code>true</code><code>);</code>

<code>    </code><code>if</code> <code>(attributes.Length &gt; 0)</code>

<code>        </code><code>attr = (DPM.Common.TableAttribute)attributes[0];</code>

<code>    </code><code>if</code> <code>(attr != </code><code>null</code><code>)</code>

<code>        </code><code>dbName = attr.TableName;</code>

<code>    </code><code>return</code> <code>dbName;</code>

<code>/// 傳回資料庫表中的所有資料列</code>

<code>public</code> <code>string</code> <code>InitDbColumns&lt;T&gt;(T model)</code>

<code>    </code><code>StringBuilder commandBuilder = </code><code>new</code> <code>StringBuilder();</code>

<code>    </code><code>DPM.Common.PropertyAttribute attr = </code><code>null</code><code>;</code>

<code>    </code><code>foreach</code> <code>(PropertyInfo property </code><code>in</code> <code>model.GetType().GetProperties())</code>

<code>        </code><code>object</code><code>[] attributes = property.GetCustomAttributes(</code><code>typeof</code><code>(DPM.Common.PropertyAttribute), </code><code>true</code><code>);</code>

<code>        </code><code>if</code> <code>(attributes.Length &gt; 0)</code>

<code>            </code><code>attr = (DPM.Common.PropertyAttribute)attributes[0];</code>

<code>        </code><code>commandBuilder.Append(attr.DbColumnName+”,”);</code>

<code>    </code><code>return</code> <code>commandBuilder.ToString().Substring(0,commandBuilder.ToString().Length-1);</code>

      本章簡單講述了ORM實作的基本思路分析及ORM架構使用的優缺點及在項目中如何合理的分析與應用。下面我們來簡單總結下本章講解的内容。

      本章主要講述了ORM的優點:減少工作流,複用性高,開發速度快,更關注業務方面的開發,将DDL操作中除了聯合查詢實作起來比較複雜外,其他的基本上都

能正常的處理。缺點:一對多或者多對多的關聯關系無法很好的滿足需求外,還有就是性能上會有一定的影響。在項目中應根據項目的業務需求來決定是否在項目中使

用ORM架構來解決問題。

        下篇我們将講解如何實作通用的資料通路層,将會詳細的介紹如何設計出通用的資料通路層,并且采用設計模中的2個原則:低耦合、高内聚等一些設計規範和原

則。歡迎大家拍磚和提出好的意見和建議。

本文轉自 hot的fans  51CTO部落格,原文連結:http://blog.51cto.com/2435232/590959