mybatis.net mysql_ADO.NET与ORM的比较(5):MyBatis实现CRUD
說明:這是一個系列文章,在前面的四篇當中周公分別講述了利用ADO.NET、NHibernate、Linq to SQL及EntityFramework來實現CRUD功能(C:Create/R:Read/U:Update/D:Delete),在這里再講述另一種框架,那就是MyBatisNet。MyBatisNet源自于iBatisNet,而iBatisNet又是受了Java平臺上的iBatis的影響。
iBatis及iBatis.NET都是利用XML描述來執行存儲過程或者SQL語句。與其它ORM框架相比,簡單易學是iBatis及iBatis.NET的最大特點,簡單易學并不意味著它們功能簡單,實際上它們能提供強大的功能。不過iBatis及iBatis.NET現在已經分別更名為MyBatis和MyBatis.NET,它們原來的官方網站http://ibatis.apache.org/上已經有聲明新的官方網站網址:http://www.mybatis.org(迄今為止,它們提供的手冊里仍是稱呼iBatis及iBatis.NET,不過這個不影響使用,在本篇中一律以MyBatisNet來稱呼)。
在這里需要說明的是MyBatis并不是一個ORM框架,像NHibernate之類的ORM框架會為你生成全部的或者絕大部分的SQL語句,但是MyBatis沒有提供這種功能。MyBatis利用你編寫的存儲過程或者SQL語句來建立對象與數據庫之間的關系映射。相比較而言NHibernate自動生成SQL語句(也可以利用HQL語言),學習難度比較大,而MyBatisNet學習起來比較容易,并且因為自己編寫SQL語句,所以比較適合數據量大對性能要求高的場合。MyBatis的工作原理圖如下:
一、準備要想在項目中使用MyBatisNet,就需要到它的官方網站http://www.mybatis.org下載相應的dll,根據官方網站的鏈接可以下載到IBatis.DataAccess.1.9.2.bin.zip和IBatis.DataMapper.1.6.2.bin.zip兩個壓縮文件,在這個壓縮文件中包含了幾乎我們需要的所有dll文件(如果使用MySQL等其它數據庫,可能需要到數據庫廠商網站下載相應的dll驅動),包含如下文件:
Castle.DynamicProxy.dll
IBatisNet.Common.dll
IBatisNet.Common.Logging.Log4Net.dll
IBatisNet.DataAccess.dll
IBatisNet.DataMapper.dll
log4net.dll
同時MyBatis還提供了一些輔助文件,如IBatisNet.Common.Logging.Log4Net.xml、IBatisNet.Common.xml、IBatisNet.DataAccess.xml、log4net.xml及IBatisNet.DataMapper.xml,將這些文件拷貝到VS的相應目錄就可以在編寫代碼時獲得程序的API說明,這個位置就是你的.NET Framework的安裝目錄,比如系統盤是C盤,這個位置就是C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\zh-CN。除此之外,還有一些xsd文件,如provider.xsd、SqlMap.xsd及SqlMapConfig.xsd,這些文件都是對應的xml文件的驗證文件,利用這些文件就可以在VS中編輯這些文件時獲得智能感知,從而減少出錯的機會。假設你的系統是安裝在C盤,如果你使用的是VS2005,那么就把這些文件拷貝到C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas;如果你使用的是VS2008,那么就拷貝到C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas;如果你使用的是VS2010,那么就拷貝到C:\Program Files\Microsoft Visual Studio 10.0\Xml\Schemas。
除了上面的準備工作之外,我們還需要幾個配置文件,分別如下:
Providers.config文件這個文件可以從下載的MyBatis壓縮包中找到,它包含了常用數據庫驅動程序清單,里面一個典型的節點如下:
name="sqlServer2005"
enabled="true"
description="Microsoft?SQL?Server,?provider?V2.0.0.0?in?framework?.NET?V2.0"
assemblyName="System.Data,?Version=2.0.0.0,?Culture=neutral,?PublicKeyToken=b77a5c561934e089"
connectionClass="System.Data.SqlClient.SqlConnection"
commandClass="System.Data.SqlClient.SqlCommand"
parameterClass="System.Data.SqlClient.SqlParameter"
parameterDbTypeClass="System.Data.SqlDbType"
parameterDbTypeProperty="SqlDbType"
dataAdapterClass="System.Data.SqlClient.SqlDataAdapter"
commandBuilderClass="?System.Data.SqlClient.SqlCommandBuilder"
usePositionalParameters="false"
useParameterPrefixInSql="true"
useParameterPrefixInParameter="true"
parameterPrefix="@"
allowMARS="true"
/>
這段XML代碼想必大家也能猜得到大部分的屬性的意思,在這里周公只講兩個需要注意的地方,一個是enabled屬性,如果要啟用某個數據庫驅動就要將它的值設為true,還有一個就是parameterPrefix屬性,表示參數化SQL語句中參數的前綴。
SqlMap.config文件這是一個有關當前數據庫信息及實體映射文件配置的文件。在這個文件里我們可以指定數據庫連接的信息(賬號、密碼及主機等),還可以指定實體映射文件。
關于數據庫連接的信息可以采用如下方式的配置:
首先在節點配置有關數據庫連接的信息,在本實例中周公的配置如下:
上面的大部分屬性的意思可以猜測得出來,唯一周公覺得需要說明的是selectKey屬性,這是解決插入記錄之后獲取自增字段主鍵的值的,在不同的數據庫中這個SQL語句可能會不同。
接著在節點中使用這些屬性,在周公的運行環境中節點值如下:
當然,你也可以采用那種被注釋的方式,也就是直接將連接字符串寫好,而不是采用未注釋的方式,不過個人感覺未注釋的方式稍微容易維護一些,一旦數據庫連接信息發生變動,集中修改節點中的值就可以了。
最后需要解決的是實體映射文件的問題。和NHibernate一樣,MyBatis也是通過XML文件來解決數據記錄與實體之間的映射關系的,關于這些映射文件如何編寫周公稍后再說。這里要說的是在SqlMap.config文件中可以有兩種方式引入外部的文件,一種是通過資源的方式,在文件中表現為resource,如,另外一種嵌入式資源的方式,在文件中表現為embedded,如,這就需要將該文件設置為嵌入式資源,如下圖所示:
在本項目中有一個實體類,它就是MyBatisNetDemo程序集中的UserInfo類,它對應的XML映射文件是項目中的MapFiles文件下的UserInfo.xml。
在SqlMap.config文件中這部分的配置如下:
App.config文件為了調試時能得到運行過程中的相關信息,需要配置Log4Net,關于Log4Net的用法周公博客上有詳盡的說明,這里就不在贅述了。在本項目中App.config文件的內容如下:
做了上面的配置之外,還需要添加相關的dll引用,在本項目中所使用到的dll引用如下圖所示:
至此,我們已經做好了所有的準備工作,可以進行下一步的編碼工作了。
二、編碼編寫實體類代碼
在本項目中采用的數據表結構與本系列的第一篇一樣(便于比較),如下:
CREATETABLE[dbo].[UserInfo](
[UserID]?[int]?IDENTITY(1,1)NOTNULL,
[UserName]?[varchar](20)COLLATEChinese_PRC_CI_ASNOTNULL,
[RealName]?[nvarchar](8)COLLATEChinese_PRC_CI_ASNOTNULL,
[Age]?[tinyint]NOTNULL,
[Sex]?[bit]NOTNULL,
[Mobile]?[char](11)COLLATEChinese_PRC_CI_ASNULL,
[Phone]?[char](11)COLLATEChinese_PRC_CI_ASNULL,
[Email]?[varchar](50)COLLATEChinese_PRC_CI_ASNOTNULL,
CONSTRAINT[PK_UserInfo]PRIMARYKEYCLUSTERED
(
[UserID]ASC
)WITH(IGNORE_DUP_KEY?=OFF)
)
所對應的實體類的代碼如下:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceMyBatisNetDemo
{
publicclassUserInfo
{
///?
///?用戶編號
///?
publicintUserID?{get;set;?}
///?
///?用戶名
///?
publicstringUserName?{get;set;?}
///?
///?真實姓名
///?
publicstringRealName?{get;set;?}
///?
///?年齡
///?
publicbyteAge?{get;set;?}
///?
///?性別
///?
publicboolSex?{get;set;?}
///?
///?電子郵件
///?
publicstringEmail?{get;set;?}
///?
///?手機號
///?
publicstringMobile?{get;set;?}
///?
///?電話
///?
publicstringPhone?{get;set;?}
}
}
編寫映射文件前面說到了映射文件是數據庫記錄與實體類之間的橋梁,它指示了數據庫字段與實體類屬性之間如何建立聯系,并且還指示了MyBatis如何去操作數據庫。在本項目中的UserInfo.xml文件內容如下:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
SELECT?UserID
,UserName
,RealName
,Age
,Sex
,Mobile
,Phone
FROM?UserInfo
]]>
where?UserID?=?#value#
]]>
where?UserName?=?#value#
]]>
SELECT
COUNT(UserID)
FROM?UserInfo
]]>
SELECT?MAX(UserID)
FROM?UserInfo
]]>
${selectKey}
insert?into?UserInfo
(UserName,RealName,Age,Sex,Mobile,Phone,Email)
values
(#UserName#,#RealName#,#Age#,#Sex#,#Mobile#,#Phone#,#Email#)
]]>
UPDATE?UserInfo?SET
UserName=#UserName#,
RealName?=#RealName#,
Age?=#Age#,
Sex=#Sex#,
Mobile=#Mobile#,
Phone=#Phone#,
Email=#Email#
WHERE
UserID=#UserID#
]]>
delete?from?UserInfo
where
UserID?=?#value#
]]>
為了便于維護和管理,這個文件放在了新建的MapFiles文件夾下。在本文件中出現的節點有如下:
:為了便于編碼,可以給實體類是一個別名,例如就是說明在本文件中UserInfo是MyBatisNetDemo程序集中MyBatisNetDemo.UserInfo類的別名。
:包含一個到多個節點,每個節點表示實體類到數據庫字段的映射,比如我們需要一個完整的數據庫字段到實體類的映射就可以用上面的表示方式,在某些情況下我們可能只需要數據庫中的某幾個字段到實體類的某幾個屬性的映射,那么可以再建一個節點表示這種映射,這些節點所表示的映射可以在下面的配置中用上。
:可以包含多個、、、節點,每個、、、中可以包含parameterClass及resultMap和resultClass屬性,在上面的例子中可以看到這三個屬性的值出現過string、int和UserInfo,這些是根據實際情況來的。這些代碼本身比較容易理解,限于篇幅在這里舉例解釋其中的一個:
where UserID = #value#
]]>
parameterClass="int"表示傳入的參數值為int類型,resultMap="SelectAllResult" extends="SelectAllUser"表示數據庫字段與實體類的映射關系如同id為SelectAllResult的節點所指示的那樣,extends="SelectAllUser"表示它的SELECT子句前面部分和id為SelectAllUser的節點一致,也就是完整的SQL語句是:
SELECT UserID
,UserName
,RealName
,Age
,Sex
,Mobile
,Phone
FROM UserInfo
where UserID = #value#
其中的#value#是占位符,將來會被傳入的int類型的參數值替換。
如果傳入的參數是實體類,如id為UpdateUserInfo的節點所示,在SQL語句中出現了#RealName#等字符串,這些表示是UserInfo的屬性。還有一點需要說明的是在節點中的${selectKey}的值在運行時將會被SqlMap.config中定義的這句所替換。
注意,有時候SQL語句中會出現“”這樣與XML文件本身相沖突的特殊字符,為了避免出現這種這種情況,我們可以將SQL語句寫在“”之間。
編寫CRUD類通過上面的工作我們配置了數據庫連接并建立了數據庫字段與實體類屬性之間的聯系,現在需要編寫一個類來操作數據庫,在本實例中的CRUD類的代碼如下:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingIBatisNet.DataMapper.Configuration;
usingIBatisNet.DataAccess;
usingIBatisNet.DataMapper;
namespaceMyBatisNetDemo
{
publicclassMyBatisNetCRUD
{
privatestaticSqlMapper?sqlMapper?=null;
staticMyBatisNetCRUD()
{
DomSqlMapBuilder?builder?=newDomSqlMapBuilder();
sqlMapper?=?builder.Configure()asSqlMapper;
}
///?
///?統計用戶總數
///?
///?
publicintCount()
{
intresult?=?sqlMapper.QueryForObject("SelectUserInfoCount",null);
returnresult;
}
///?
///?創建用戶
///?
///?用戶實體
///?
publicboolCreate(UserInfo?info)
{
//object?o=sqlMapper.Insert("InsertUserInfo",?info);
//int?id=(int)o;
//return?id>0;
intid?=?(int)sqlMapper.Insert("InsertUserInfo",?info);
returnid?>?0;
}
///?
///?讀取用戶信息
///?
///?用戶編號
///?
publicUserInfo?Read(intuserId)
{
UserInfo?info?=?sqlMapper.QueryForObject("SelectByUserId",?userId);
returninfo;
}
publicIList?GetUserList()
{
IList?userList?=?sqlMapper.QueryForList("SelectAllUser",null);
returnuserList;
}
publicIList?GetUserList(intindex,intsize)
{
stringconnectionString=sqlMapper.DataSource.ConnectionString;
Console.WriteLine(connectionString);
IList?userList?=?sqlMapper.QueryForList("SelectAllUser",null,index,size);
returnuserList;
}
///?
///?更新用戶信息
///?
///?用戶實體
///?
publicboolUpdate(UserInfo?info)
{
intresult?=?sqlMapper.Update("UpdateUserInfo",?info);
returnresult?>?0;
}
///?
///?刪除用戶
///?
///?用戶編號
///?
publicboolDelete(intuserId)
{
intresult?=?sqlMapper.Delete("DeleteUserInfo",?userId);
returnresult?>?0;
}
///?
///?獲取用戶表中編號最大的用戶
///?
///?
publicintGetMaxUserId()
{
intresult?=?sqlMapper.QueryForObject("SelectMaxUserId",null);
returnresult;
}
}
}
為了對比,在這個CRUD類中實現的功能與前幾篇中實現的功能一致,在代碼中我們首先要實例化SqlMapper類,獲得了這個類的實例后就可以利用它來操作數據庫了。在方法中第一個字符串類型的參數都是我們在UserInfo.xml中配置的那些、、、節點的id(這些id自然在整個項目中不能重復命名了),后面的參數是根據執行該id對應的SQL語句所需要的參數。
注意:在本項目中周公將providers.config和SqlMap.config文件設置成復制到輸出目錄,而將UserInfo.xml文件設置為嵌入式資源了。在實際操作中要注意這一點。整個項目的結構如下:
三、單元測試代碼為了照顧很多仍在使用NUnit作為單元測試工具的開發人員的習慣,我們的單元測試代碼針對NUnit2.5.3,代碼如下:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingNUnit.Framework;
usingMyBatisNetDemo;
namespaceNUnitTest
{
[TestFixture]
publicclassMyBatisNetTest
{
privateMyBatisNetCRUD?instance?=null;
[SetUp]
publicvoidInitialize()
{
instance?=newMyBatisNetCRUD();
}
[Test]
publicvoidCount()
{
Assert.Greater(instance.Count(),?0);
}
[Test]
publicvoidCreate()
{
UserInfo?userInfo?=newMyBatisNetDemo.UserInfo?{?UserName?="www1",?Age?=?40,?Email?="zhoufoxcn1@gmail.com",?Mobile?="13567891234",?Phone?="02786543210",?RealName?="test",?Sex?=false};
Assert.True(instance.Create(userInfo));
}
[Test]
publicvoidRead()
{
UserInfo?info?=instance.Read(2);
//Assert.IsNotNull(info);
Assert.AreEqual(info.UserID,?2);
}
[Test]
publicvoidGetUserList()
{
IList?userList?=?instance.GetUserList();
Assert.Greater(userList.Count,0);
}
[Test]
publicvoidGetUserListPaging()
{
IList?userList?=?instance.GetUserList(10,20);
Assert.Greater(userList.Count,?0);
}
[Test]
publicvoidUpdate()
{
UserInfo?info?=?instance.Read(1);
Assert.True(instance.Update(info));
}
[Test]
publicvoidDelete()
{
intmaxUserId?=?instance.GetMaxUserId();
Assert.True(instance.Delete(maxUserId));
}
[Test]
publicvoidGetMaxUserId()
{
intresult?=?instance.GetMaxUserId();
Assert.True(result?>?0);
}
}
}
上面的代碼在NUnit2.5.3中測試通過。
四、總結作為一種數據庫與實體類的映射框架二不是一個ORM框架,和ADO.NET相比MyBatis不用太多考慮ADO.NET的細節(比如是用SqlConnection還是用OleDbConnecion,還有如何打開和關閉數據庫連接等),因而比較容易跨數據庫;和NHibernate這樣的ORM相比它又能讓我們更多地操縱如何與數據庫交互,性能上更容易控制一些(前提是必須有比較精通書庫的開發人員或者DBA),同時它也比NHibernate更容易學習一些。
當然MyBatis也有它的一些不足,比如在UserInfo.xml中我們要編寫大量的SQL語句和添加很多、、、節點,當然這些不足可以通過一個自動代碼生成工具來解決。
從個人選擇上來說,我更愿意選擇MyBatisNet,今后的項目中我可能會更多的使用MyBatisNet。另外從我一個在銀行從事開發的朋友得到的反饋,他們用Java做銀行項目選擇的也是MyBatisNet的Java版——MyBatis,這是出于對性能的考慮。
沒有最好的,只有最合適的。如何選擇合適的技術,取決于我們隊將要運行的環境的判斷,這來自于經驗分析。周公
2010-12-17
廣告:本人現在正在參加“2010年度十大杰出IT博客大賽”,歡迎大家前去捧場投下您寶貴的一票!網址是:http://2010blog.51cto.com/792419,歡迎大家每天投票支持周公。
總結
以上是生活随笔為你收集整理的mybatis.net mysql_ADO.NET与ORM的比较(5):MyBatis实现CRUD的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lisp用entmake生产圆柱体_使用
- 下一篇: java snmp walk_snmpw