现在的位置: 首页 > 综合 > 正文

ADO.NET与ORM的比较(5):MyBatis实现CRUD

2012年11月13日 ⁄ 综合 ⁄ 共 13283字 ⁄ 字号 评论关闭

说明:这是一个系列文章,在前面的四篇当中周公分别讲述了利用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压缩包中找到,它包含了常用数据库驱动程序清单,里面一个典型的节点如下:


  1. <
    provider
     
  2.     name = "sqlServer2005"  
  3.     enabled = "true"  
  4.     description = "Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0"    
  5.     assemblyName = "System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"    
  6.     connectionClass = "System.Data.SqlClient.SqlConnection"    
  7.     commandClass = "System.Data.SqlClient.SqlCommand"  
  8.     parameterClass = "System.Data.SqlClient.SqlParameter"  
  9.     parameterDbTypeClass = "System.Data.SqlDbType"  
  10.     parameterDbTypeProperty = "SqlDbType"  
  11.     dataAdapterClass = "System.Data.SqlClient.SqlDataAdapter"  
  12.     commandBuilderClass = " System.Data.SqlClient.SqlCommandBuilder"  
  13.     usePositionalParameters  =  "false"  
  14.     useParameterPrefixInSql  =  "true"  
  15.     useParameterPrefixInParameter  =  "true"    
  16.     parameterPrefix = "@"  
  17.     allowMARS = "true"  
  18.    />  

这段XML代码想必大家也能猜得到大部分的属性的意思,在这里周公只讲两个需要注意的地方,一个是enabled属性,如果要启用某个数据库驱动就要将它的值设为true,还有一个就是parameterPrefix属性,表示参数化SQL语句中参数的前缀。

SqlMap.config文件
这是一个有关当前数据库信息及实体映射文件配置的文件。在这个文件里我们可以指定数据库连接的信息(账号、密码及主机等),还可以指定实体映射文件。
关于数据库连接的信息可以采用如下方式的配置:
首先在<properties>节点配置有关数据库连接的信息,在本实例中周公的配置如下:


  1. <
    properties
    >
     
  2.    < property   key = "userid"   value = "sa"   />  
  3.    < property   key = "password"   value = "root"   />  
  4.    < property   key = "database"   value = "AspNetStudy"   />  
  5.    < property   key = "datasource"   value = "netskycn\SQL2005"   />  
  6.    < property   key = "selectKey"   value = "select @@IDENTITY as value"   />  
  7.    < property   key = "directory"   value = "MapFiles"   />  
  8.    < property   key = "useStatementNamespaces"   value = "false"   />  
  9. </ properties >  

上面的大部分属性的意思可以猜测得出来,唯一周公觉得需要说明的是selectKey属性,这是解决插入记录之后获取自增字段主键的值的,在不同的数据库中这个SQL语句可能会不同。
接着在<database>节点中使用这些属性,在周公的运行环境中<database>节点值如下:


  1. <
    database
    >
     
  2.    < provider   name = "sqlServer2005" />  
  3.    <!--<dataSource name="iBatisNet" connectionString="data source=netskycn\SQL2005;database=AspNetStudy;user id=sa;password=root;"/>-->  
  4.    < dataSource   name = "iBatisNet"   connectionString = "data source=${datasource};database=${database};user id=${userid};password=${password};" />  
  5. </ database >  

当然,你也可以采用那种被注释的方式,也就是直接将连接字符串写好,而不是采用未注释的方式,不过个人感觉未注释的方式稍微容易维护一些,一旦数据库连接信息发生变动,集中修改<properties>节点中的值就可以了。
最后需要解决的是实体映射文件的问题。和NHibernate一样,MyBatis也是通过XML文件来解决数据记录与实体之间的映射关系的,关于这些映射文件如何编写周公稍后再说。这里要说的是在SqlMap.config文件中可以有两种方式引入外部的文件,一种是通过资源的方式,在文件中表现为resource,如<providers resource="providers.config"/>,另外一种嵌入式资源的方式,在文件中表现为embedded,如<sqlMap embedded="MapFiles.UserInfo.xml,MyBatisNetDemo"/>,这就需要将该文件设置为嵌入式资源,如下图所示:

在本项目中有一个实体类,它就是MyBatisNetDemo程序集中的UserInfo类,它对应的XML映射文件是项目中的MapFiles文件下的UserInfo.xml。
在SqlMap.config文件中这部分的配置如下:


  1. <
    sqlMaps
    >
     
  2.    < sqlMap   embedded = "MapFiles.UserInfo.xml,MyBatisNetDemo" />  
  3. </ sqlMaps >  

App.config文件
为了调试时能得到运行过程中的相关信息,需要配置Log4Net,关于Log4Net的用法周公博客上有详尽的说明,这里就不在赘述了。在本项目中App.config文件的内容如下:


  1. <?
    xml
     
    version
    =
    "1.0"
     
    encoding
    =
    "utf-8"
     
    ?>
     
  2. < configuration >  
  3.    < configSections >  
  4.      < sectionGroup   name = "iBATIS" >  
  5.        < section   name = "logging"   type = "IBatisNet.Common.Logging.ConfigurationSectionHandler, IBatisNet.Common"   />  
  6.      </ sectionGroup >  
  7.      < section   name = "log4net"   type = "log4net.Config.Log4NetConfigurationSectionHandler, log4net"   />  
  8.    </ configSections >  
  9.    < iBATIS >  
  10.      < logging >  
  11.        < logFactoryAdapter   type = "IBatisNet.Common.Logging.Impl.Log4NetLoggerFA, IBatisNet.Common.Logging.Log4Net" >  
  12.          < arg   key = "configType"   value = "inline"   />  
  13.          < arg   key  = "showLogName"   value = "true"   />  
  14.          < arg   key  = "showDataTime"   value = "true"   />  
  15.          < arg   key  = "level"   value = "ALL"   />  
  16.          < arg   key  = "dateTimeFormat"   value = "yyyy/MM/dd HH:mm:ss:SSS"   />  
  17.        </ logFactoryAdapter >  
  18.      </ logging >  
  19.    </ iBATIS >  
  20.    <!-- 下面的节点定义log4net -->  
  21.    < log4net >  
  22.      <!-- 定义输出的appenders -->  
  23.      < appender   name = "RollingLogFileAppender"   type = "log4net.Appender.RollingFileAppender" >  
  24.        < param   name = "File"   value = "iBatisNet_log.txt"   />  
  25.        < param   name = "AppendToFile"   value = "true"   />  
  26.        < param   name = "MaxSizeRollBackups"   value = "2"   />  
  27.        < param   name = "MaximumFileSize"   value = "100KB"   />  
  28.        < param   name = "RollingStyle"   value = "Size"   />  
  29.        < param   name = "StaticLogFileName"   value = "true"   />  
  30.        < layout   type = "log4net.Layout.PatternLayout" >  
  31.          < param   name = "Header"   value = "[Header]\r\n"   />  
  32.          < param   name = "Footer"   value = "[Footer]\r\n"   />  
  33.          < param   name = "ConversionPattern"   value = "%d [%t] %-5p %c [%x] - %m%n"   />  
  34.        </ layout >  
  35.      </ appender >  
  36.      < appender   name = "ConsoleAppender"   type = "log4net.Appender.ConsoleAppender" >  
  37.        < layout   type = "log4net.Layout.PatternLayout" >  
  38.          < param   name = "ConversionPattern"   value = "%d [%t] %-5p %c [%x] &lt;%X{auth}&gt; - %m%n"   />  
  39.        </ layout >  
  40.      </ appender >  
  41.      <!-- Set root logger level to ERROR and its appenders -->  
  42.      < root >  
  43.        < level   value = "DEBUG"   />  
  44.        < appender-ref   ref = "RollingLogFileAppender"   />  
  45.        < appender-ref   ref = "ConsoleAppender"   />  
  46.      </ root >  
  47.  
  48.      <!-- Print only messages of level DEBUG or above in the packages -->  
  49.      < logger   name = "IBatisNet.DataMapper.Configuration.Cache.CacheModel" >  
  50.        < level   value = "DEBUG"   />  
  51.      </ logger >  
  52.      < logger   name = "IBatisNet.DataMapper.Configuration.Statements.PreparedStatementFactory" >  
  53.        < level   value = "DEBUG"   />  
  54.      </ logger >  
  55.      < logger   name = "IBatisNet.DataMapper.LazyLoadList" >  
  56.        < level   value = "DEBUG"   />  
  57.      </ logger >  
  58.      < logger   name = "IBatisNet.DataAccess.DaoSession" >  
  59.        < level   value = "DEBUG"   />  
  60.      </ logger >  
  61.      < logger   name = "IBatisNet.DataMapper.SqlMapSession" >  
  62.        < level   value = "DEBUG"   />  
  63.      </ logger >  
  64.      < logger   name = "IBatisNet.Common.Transaction.TransactionScope" >  
  65.        < level   value = "DEBUG"   />  
  66.      </ logger >  
  67.      < logger   name = "IBatisNet.DataAccess.Configuration.DaoProxy" >  
  68.        < level   value = "DEBUG"   />  
  69.      </ logger >  
  70.    </ log4net >  
  71. </ configuration >  

做了上面的配置之外,还需要添加相关的dll引用,在本项目中所使用到的dll引用如下图所示:

至此,我们已经做好了所有的准备工作,可以进行下一步的编码工作了。

二、编码
编写实体类代码
在本项目中采用的数据表结构与本系列的第一篇一样(便于比较),如下:


  1. CREATE
     
    TABLE
     [dbo].[UserInfo](  
  2.         [UserID] [ int ] IDENTITY(1,1)  NOT   NULL ,  
  3.         [UserName] [ varchar ](20)  COLLATE  Chinese_PRC_CI_AS  NOT   NULL ,  
  4.         [RealName] [nvarchar](8)  COLLATE  Chinese_PRC_CI_AS  NOT   NULL ,  
  5.         [Age] [tinyint]  NOT   NULL ,  
  6.         [Sex] [ bit NOT   NULL ,  
  7.         [Mobile] [ char ](11)  COLLATE  Chinese_PRC_CI_AS  NULL ,  
  8.         [Phone] [ char ](11)  COLLATE  Chinese_PRC_CI_AS  NULL ,  
  9.         [Email] [ varchar ](50)  COLLATE  Chinese_PRC_CI_AS  NOT   NULL ,  
  10.       CONSTRAINT  [PK_UserInfo]  PRIMARY   KEY  CLUSTERED   
  11.     (  
  12.         [UserID]  ASC  
  13.     ) WITH  (IGNORE_DUP_KEY =  OFF )  
  14.     ) 

所对应的实体类的代码如下:


  1. using
     System;  
  2. using  System.Collections.Generic;  
  3. using  System.Text;  
  4.  
  5. namespace  MyBatisNetDemo  
  6. {  
  7.      public   class  UserInfo  
  8.     {  
  9.          /// <summary>  
  10.          /// 用户编号  
  11.          /// </summary>  
  12.          public   int  UserID {  get set ; }  
  13.          /// <summary>  
  14.          /// 用户名  
  15.          /// </summary>  
  16.          public   string  UserName {  get set ; }  
  17.          /// <summary>  
  18.          /// 真实姓名  
  19.          /// </summary>  
  20.          public   string  RealName {  get set ; }  
  21.          /// <summary>  
  22.          /// 年龄  
  23.          /// </summary>  
  24.          public   byte  Age {  get set ; }  
  25.          /// <summary>  
  26.          /// 性别  
  27.          /// </summary>  
  28.          public   bool  Sex {  get set ; }  
  29.          /// <summary>  
  30.          /// 电子邮件  
  31.          /// </summary>  
  32.          public   string  Email {  get set ; }  
  33.          /// <summary>  
  34.          /// 手机号  
  35.          /// </summary>  
  36.          public   string  Mobile {  get set ; }  
  37.          /// <summary>  
  38.          /// 电话  
  39.          /// </summary>  
  40.          public   string  Phone {  get set ; }  
  41.     }  

编写映射文件
前面说到了映射文件是数据库记录与实体类之间的桥梁,它指示了数据库字段与实体类属性之间如何建立联系,并且还指示了MyBatis如何去操作数据库。在本项目中的UserInfo.xml文件内容如下:


  1. <?
    xml
     
    version
    =
    "1.0"
     
    encoding
    =
    "utf-8"
     
    ?>
     
  2. < sqlMap   namespace = "UserInfo"   xmlns = "http://ibatis.apache.org/mapping"  
  3. xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"   >  
  4.  
  5.    < alias >  
  6.      < typeAlias   alias = "UserInfo"   type = "MyBatisNetDemo.UserInfo,MyBatisNetDemo"   />  
  7.    </ alias >  
  8.  
  9.    < resultMaps >  
  10.      < resultMap   id = "SelectAllResult"   class = "UserInfo" >  
  11.        < result   property = "UserID"   column = "UserID"   dbType = "int"   type = "int"   />  
  12.        < result   property = "UserName"   column = "UserName"   dbType = "varchar"   type = "string"   />  
  13.        < result   property = "RealName"   column = "RealName"   dbType = "varchar"   type = "string"   />  
  14.        < result   property = "Sex"   column = "Sex"   dbType = "bit"   type = "bool"   />  
  15.        < result   property = "Mobile"   column = "Mobile"   dbType = "varchar"   type = "string"   />  
  16.        < result   property = "Phone"   column = "Phone"   dbType = "varchar"   type = "string"   />  
  17.        < result   property = "Email"   column = "Email"   dbType = "varchar"   type = "string" />  
  18.      </ resultMap >  
  19.    </ resultMaps >  
  20.  
  21.    < statements >  
  22.      < select   id = "SelectAllUser"   resultMap = "SelectAllResult" >  
  23.        <![CDATA[

  24.       SELECT UserID

  25.       ,UserName

  26.       ,RealName

  27.       ,Age

  28.       ,Sex

  29.       ,Mobile

  30.       ,Phone

  31.       ,Email

  32.       FROM UserInfo

  33.       ]]>
     
  34.      </ select >  
  35.  
  36.      < select   id = "SelectByUserId"   parameterClass = "int"   resultMap = "SelectAllResult"   extends = "SelectAllUser" >  
  37.        <![CDATA[

  38.       where UserID = #value#

  39.       ]]>
     
  40.      </ select >  
  41.  
  42.      < select   id = "SelectByUserName"   resultClass = "UserInfo"   parameterClass = "string"   resultMap = "SelectAllResult"   extends = "SelectAllUser" >  
  43.        <![CDATA[

  44.       where UserName = #value#

  45.       ]]>
     
  46.      </ select >  
  47.  
  48.      < select   id = "SelectUserInfoCount"   resultClass = "int" >  
  49.        <![CDATA[

  50.       SELECT

  51.       COUNT(UserID)

  52.       FROM UserInfo

  53.       ]]>
     
  54.      </ select >  
  55.  
  56.      < select   id = "SelectMaxUserId"   resultClass = "int" >  
  57.        <![CDATA[

  58.       SELECT MAX(UserID)

  59.       FROM UserInfo

  60.       ]]>
     
  61.      </ select >  
  62.  
  63.      < insert   id = "InsertUserInfo"   parameterClass = "UserInfo"   >  
  64.        < selectKey   property = "UserID"   type = "post"   resultClass = "int" >  
  65.         ${selectKey}  
  66.        </ selectKey >  
  67.        <![CDATA[

  68.       insert into UserInfo

  69.       (UserName,RealName,Age,Sex,Mobile,Phone,Email)

  70.       values

  71.       (#UserName#,#RealName#,#Age#,#Sex#,#Mobile#,#Phone#,#Email#)

  72.       ]]>
     
  73.      </ insert >  
  74.  
  75.      < update   id = "UpdateUserInfo"   parameterClass = "UserInfo" >  
  76.        <![CDATA[

  77.       UPDATE UserInfo SET

  78.       UserName=#UserName#,

  79.       RealName =#RealName#,

  80.       Age =#Age#,

  81.       Sex=#Sex#,

  82.       Mobile=#Mobile#,

  83.       Phone=#Phone#,

  84.       Email=#Email#

  85.       WHERE

  86.       UserID=#UserID#

  87.       ]]>
     
  88.      </ update >  
  89.  
  90.      < delete   id = "DeleteUserInfo"   parameterClass = "int" >  
  91.        <![CDATA[

  92.       delete from UserInfo

  93.       where

  94.       UserID = #value#

  95.       ]]>
     
  96.      </ delete >  
  97.  
  98.    </ statements >  
  99. </ sqlMap >  

为了便于维护和管理,这个文件放在了新建的MapFiles文件夹下。在本文件中出现的节点有如下:
<alias>:为了便于编码,可以给实体类是一个别名,例如<typeAlias alias="UserInfo" type="MyBatisNetDemo.UserInfo,MyBatisNetDemo" />就是说明在本文件中UserInfo是MyBatisNetDemo程序集中MyBatisNetDemo.UserInfo类的别名。
<resultMaps>:包含一个到多个<resultMap>节点,每个节点表示实体类到数据库字段的映射,比如我们需要一个完整的数据库字段到实体类的映射就可以用上面的表示方式,在某些情况下我们可能只需要数据库中的某几个字段到实体类的某几个属性的映射,那么可以再建一个<resultMap>节点表示这种映射,这些<resultMap>节点所表示的映射可以在下面的配置中用上。
<statements>:可以包含多个<select>、<insert>、<update>、<delete>节点,每个<select>、<insert>、<update>、<delete>中可以包含parameterClass及resultMap和resultClass属性,在上面的例子中可以看到这三个属性的值出现过string、int和UserInfo,这些是根据实际情况来的。这些代码本身比较容易理解,限于篇幅在这里举例解释其中的一个:
<select id="SelectByUserId" parameterClass="int" resultMap="SelectAllResult" extends="SelectAllUser">
      <![CDATA[
      where UserID = #value#
      ]]>
    </select>
parameterClass="int"表示传入的参数值为int类型,resultMap="SelectAllResult" extends="SelectAllUser"表示数据库字段与实体类的映射关系如同id为SelectAllResult的<resultMap>节点所指示的那样,extends="SelectAllUser"表示它的SELECT子句前面部分和id为SelectAllUser的<select>节点一致,也就是完整的SQL语句是:
SELECT UserID
      ,UserName
      ,RealName
      ,Age
      ,Sex
      ,Mobile
      ,Phone
      ,Email
      FROM UserInfo
where UserID = #value#
其中的#value#是占位符,将来会被传入的int类型的参数值替换。
如果传入的参数是实体类,如id为UpdateUserInfo的<update>节点所示,在SQL语句中出现了#RealName#等字符串,这些表示是UserInfo的属性。还有一点需要说明的是在<insert>节点中的${selectKey}的值在运行时将会被SqlMap.config中定义的<property key="selectKey" value="select @@IDENTITY as value" />这句所替换。
注意,有时候SQL语句中会出现“<”及“>”这样与XML文件本身相冲突的特殊字符,为了避免出现这种这种情况,我们可以将SQL语句写在“<![CDATA[”与“]]>”之间。

编写CRUD类
通过上面的工作我们配置了数据库连接并建立了数据库字段与实体类属性之间的联系,现在需要编写一个类来操作数据库,在本实例中的CRUD类的代码如下:


  1. using
     System;  

抱歉!评论已关闭.