说明:这是一个系列文章,在前面的四篇当中周公分别讲述了利用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. <!-- Print only messages of level DEBUG or above in the packages -->
  48. <logger name="IBatisNet.DataMapper.Configuration.Cache.CacheModel">
  49. <level value="DEBUG" />
  50. </logger>
  51. <logger name="IBatisNet.DataMapper.Configuration.Statements.PreparedStatementFactory">
  52. <level value="DEBUG" />
  53. </logger>
  54. <logger name="IBatisNet.DataMapper.LazyLoadList">
  55. <level value="DEBUG" />
  56. </logger>
  57. <logger name="IBatisNet.DataAccess.DaoSession">
  58. <level value="DEBUG" />
  59. </logger>
  60. <logger name="IBatisNet.DataMapper.SqlMapSession">
  61. <level value="DEBUG" />
  62. </logger>
  63. <logger name="IBatisNet.Common.Transaction.TransactionScope">
  64. <level value="DEBUG" />
  65. </logger>
  66. <logger name="IBatisNet.DataAccess.Configuration.DaoProxy">
  67. <level value="DEBUG" />
  68. </logger>
  69. </log4net>
  70. </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] [bitNOT 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. namespace MyBatisNetDemo
  5. {
  6. public class UserInfo
  7. {
  8. /// <summary>
  9. /// 用户编号
  10. /// </summary>
  11. public int UserID { getset; }
  12. /// <summary>
  13. /// 用户名
  14. /// </summary>
  15. public string UserName { getset; }
  16. /// <summary>
  17. /// 真实姓名
  18. /// </summary>
  19. public string RealName { getset; }
  20. /// <summary>
  21. /// 年龄
  22. /// </summary>
  23. public byte Age { getset; }
  24. /// <summary>
  25. /// 性别
  26. /// </summary>
  27. public bool Sex { getset; }
  28. /// <summary>
  29. /// 电子邮件
  30. /// </summary>
  31. public string Email { getset; }
  32. /// <summary>
  33. /// 手机号
  34. /// </summary>
  35. public string Mobile { getset; }
  36. /// <summary>
  37. /// 电话
  38. /// </summary>
  39. public string Phone { getset; }
  40. }
  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. <alias>
  5. <typeAlias alias="UserInfo" type="MyBatisNetDemo.UserInfo,MyBatisNetDemo" />
  6. </alias>
  7. <resultMaps>
  8. <resultMap id="SelectAllResult" class="UserInfo">
  9. <result property="UserID" column="UserID" dbType="int" type="int" />
  10. <result property="UserName" column="UserName" dbType="varchar" type="string" />
  11. <result property="RealName" column="RealName" dbType="varchar" type="string" />
  12. <result property="Sex" column="Sex" dbType="bit" type="bool" />
  13. <result property="Mobile" column="Mobile" dbType="varchar" type="string" />
  14. <result property="Phone" column="Phone" dbType="varchar" type="string" />
  15. <result property="Email" column="Email" dbType="varchar" type="string"/>
  16. </resultMap>
  17. </resultMaps>
  18. <statements>
  19. <select id="SelectAllUser" resultMap="SelectAllResult">
  20. <![CDATA[
  21. SELECT UserID
  22. ,UserName
  23. ,RealName
  24. ,Age
  25. ,Sex
  26. ,Mobile
  27. ,Phone
  28. ,Email
  29. FROM UserInfo
  30. ]]>
  31. </select>
  32. <select id="SelectByUserId" parameterClass="int" resultMap="SelectAllResult" extends="SelectAllUser">
  33. <![CDATA[
  34. where UserID = #value#
  35. ]]>
  36. </select>
  37. <select id="SelectByUserName" resultClass="UserInfo" parameterClass="string" resultMap="SelectAllResult" extends="SelectAllUser">
  38. <![CDATA[
  39. where UserName = #value#
  40. ]]>
  41. </select>
  42. <select id="SelectUserInfoCount" resultClass="int">
  43. <![CDATA[
  44. SELECT
  45. COUNT(UserID)
  46. FROM UserInfo
  47. ]]>
  48. </select>
  49. <select id="SelectMaxUserId" resultClass="int">
  50. <![CDATA[
  51. SELECT MAX(UserID)
  52. FROM UserInfo
  53. ]]>
  54. </select>
  55. <insert id="InsertUserInfo" parameterClass="UserInfo" >
  56. <selectKey property="UserID" type="post" resultClass="int">
  57. ${selectKey}
  58. </selectKey>
  59. <![CDATA[
  60. insert into UserInfo
  61. (UserName,RealName,Age,Sex,Mobile,Phone,Email)
  62. values
  63. (#UserName#,#RealName#,#Age#,#Sex#,#Mobile#,#Phone#,#Email#)
  64. ]]>
  65. </insert>
  66. <update id="UpdateUserInfo" parameterClass="UserInfo">
  67. <![CDATA[
  68. UPDATE UserInfo SET
  69. UserName=#UserName#,
  70. RealName =#RealName#,
  71. Age =#Age#,
  72. Sex=#Sex#,
  73. Mobile=#Mobile#,
  74. Phone=#Phone#,
  75. Email=#Email#
  76. WHERE
  77. UserID=#UserID#
  78. ]]>
  79. </update>
  80. <delete id="DeleteUserInfo" parameterClass="int">
  81. <![CDATA[
  82. delete from UserInfo
  83. where
  84. UserID = #value#
  85. ]]>
  86. </delete>
  87. </statements>
  88. </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;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using IBatisNet.DataMapper.Configuration;
  5. using IBatisNet.DataAccess;
  6. using IBatisNet.DataMapper;
  7. namespace MyBatisNetDemo
  8. {
  9. public class MyBatisNetCRUD
  10. {
  11. private static SqlMapper sqlMapper = null;
  12. static MyBatisNetCRUD()
  13. {
  14. DomSqlMapBuilder builder = new DomSqlMapBuilder();
  15. sqlMapper = builder.Configure() as SqlMapper;
  16. }
  17. /// <summary>
  18. /// 统计用户总数
  19. /// </summary>
  20. /// <returns></returns>
  21. public int Count()
  22. {
  23. int result = sqlMapper.QueryForObject<int>("SelectUserInfoCount"null);
  24. return result;
  25. }
  26. /// <summary>
  27. /// 创建用户
  28. /// </summary>
  29. /// <param name="info">用户实体</param>
  30. /// <returns></returns>
  31. public bool Create(UserInfo info)
  32. {
  33. //object o=sqlMapper.Insert("InsertUserInfo", info);
  34. //int id=(int)o;
  35. //return id>0;
  36. int id = (int)sqlMapper.Insert("InsertUserInfo", info);
  37. return id > 0;
  38. }
  39. /// <summary>
  40. /// 读取用户信息
  41. /// </summary>
  42. /// <param name="userId">用户编号</param>
  43. /// <returns></returns>
  44. public UserInfo Read(int userId)
  45. {
  46. UserInfo info = sqlMapper.QueryForObject<UserInfo>("SelectByUserId", userId);
  47. return info;
  48. }
  49. public IList<UserInfo> GetUserList()
  50. {
  51. IList<UserInfo> userList = sqlMapper.QueryForList<UserInfo>("SelectAllUser"null);
  52. return userList;
  53. }
  54. public IList<UserInfo> GetUserList(int index,int size)
  55. {
  56. string connectionString=sqlMapper.DataSource.ConnectionString;
  57. Console.WriteLine(connectionString);
  58. IList<UserInfo> userList = sqlMapper.QueryForList<UserInfo>("SelectAllUser"null,index,size);
  59. return userList;
  60. }
  61. /// <summary>
  62. /// 更新用户信息
  63. /// </summary>
  64. /// <param name="info">用户实体</param>
  65. /// <returns></returns>
  66. public bool Update(UserInfo info)
  67. {
  68. int result = sqlMapper.Update("UpdateUserInfo", info);
  69. return result > 0;
  70. }
  71. /// <summary>
  72. /// 删除用户
  73. /// </summary>
  74. /// <param name="userId">用户编号</param>
  75. /// <returns></returns>
  76. public bool Delete(int userId)
  77. {
  78. int result = sqlMapper.Delete("DeleteUserInfo", userId);
  79. return result > 0;
  80. }
  81. /// <summary>
  82. /// 获取用户表中编号最大的用户
  83. /// </summary>
  84. /// <returns></returns>
  85. public int GetMaxUserId()
  86. {
  87. int result = sqlMapper.QueryForObject<int>("SelectMaxUserId"null);
  88. return result;
  89. }
  90. }
  91. }

为了对比,在这个CRUD类中实现的功能与前几篇中实现的功能一致,在代码中我们首先要实例化SqlMapper类,获得了这个类的实例后就可以利用它来操作数据库了。在方法中第一个字符串类型的参数都是我们在UserInfo.xml中配置的那些<select>、<insert>、<update>、<delete>节点的id(这些id自然在整个项目中不能重复命名了),后面的参数是根据执行该id对应的SQL语句所需要的参数。
注意:在本项目中周公将providers.config和SqlMap.config文件设置成复制到输出目录,而将UserInfo.xml文件设置为嵌入式资源了。在实际操作中要注意这一点。整个项目的结构如下:

三、单元测试代码
为了照顾很多仍在使用NUnit作为单元测试工具的开发人员的习惯,我们的单元测试代码针对NUnit2.5.3,代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using NUnit.Framework;
  6. using MyBatisNetDemo;
  7. namespace NUnitTest
  8. {
  9. [TestFixture]
  10. public class MyBatisNetTest
  11. {
  12. private MyBatisNetCRUD instance = null;
  13. [SetUp]
  14. public void Initialize()
  15. {
  16. instance = new MyBatisNetCRUD();
  17. }
  18. [Test]
  19. public void Count()
  20. {
  21. Assert.Greater(instance.Count(), 0);
  22. }
  23. [Test]
  24. public void Create()
  25. {
  26. UserInfo userInfo = new MyBatisNetDemo.UserInfo { UserName = "www1", Age = 40, Email = "zhoufoxcn1@gmail.com", Mobile = "13567891234", Phone = "02786543210", RealName = "test", Sex = false };
  27. Assert.True(instance.Create(userInfo));
  28. }
  29. [Test]
  30. public void Read()
  31. {
  32. UserInfo info =instance.Read(2);
  33. //Assert.IsNotNull(info);
  34. Assert.AreEqual(info.UserID, 2);
  35. }
  36. [Test]
  37. public void GetUserList()
  38. {
  39. IList<UserInfo> userList = instance.GetUserList();
  40. Assert.Greater(userList.Count,0);
  41. }
  42. [Test]
  43. public void GetUserListPaging()
  44. {
  45. IList<UserInfo> userList = instance.GetUserList(10,20);
  46. Assert.Greater(userList.Count, 0);
  47. }
  48. [Test]
  49. public void Update()
  50. {
  51. UserInfo info = instance.Read(1);
  52. Assert.True(instance.Update(info));
  53. }
  54. [Test]
  55. public void Delete()
  56. {
  57. int maxUserId = instance.GetMaxUserId();
  58. Assert.True(instance.Delete(maxUserId));
  59. }
  60. [Test]
  61. public void GetMaxUserId()
  62. {
  63. int result = instance.GetMaxUserId();
  64. Assert.True(result > 0);
  65. }
  66. }
  67. }

上面的代码在NUnit2.5.3中测试通过。
四、总结
作为一种数据库与实体类的映射框架二不是一个ORM框架,和ADO.NET相比MyBatis不用太多考虑ADO.NET的细节(比如是用SqlConnection还是用OleDbConnecion,还有如何打开和关闭数据库连接等),因而比较容易跨数据库;和NHibernate这样的ORM相比它又能让我们更多地操纵如何与数据库交互,性能上更容易控制一些(前提是必须有比较精通书库的开发人员或者DBA),同时它也比NHibernate更容易学习一些。
当然MyBatis也有它的一些不足,比如在UserInfo.xml中我们要编写大量的SQL语句和添加很多<select>、<insert>、<update>、<delete>节点,当然这些不足可以通过一个自动代码生成工具来解决。
从个人选择上来说,我更愿意选择MyBatisNet,今后的项目中我可能会更多的使用MyBatisNet。另外从我一个在银行从事开发的朋友得到的反馈,他们用Java做银行项目选择的也是MyBatisNet的Java版——MyBatis,这是出于对性能的考虑。
没有最好的,只有最合适的。如何选择合适的技术,取决于我们队将要运行的环境的判断,这来自于经验分析。周公
2010-12-17


广告:本人现在正在参加“2010年度十大杰出IT博客大赛”,欢迎大家前去捧场投下您宝贵的一票!网址是:http://2010blog.51cto.com/792419,欢迎大家每天投票支持周公。

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

  1. mybatis.net mysql_ADO.NET与ORM的比较(5):MyBatis实现CRUD

    说明:这是一个系列文章,在前面的四篇当中周公分别讲述了利用ADO.NET.NHibernate.Linq to SQL及EntityFramework来实现CRUD功能(C:Create/R:Read ...

  2. ADO.NET与ORM的比较(4):EntityFramework实现CRUD

    说明:个人感觉在Java领域大型开发都离不了ORM的身影,所谓的SSH就是Spring+Struts+Hibernate,除了在学习基础知识的时候被告知可以使用JDBC操作数据库之外,大量的书籍中都是 ...

  3. MyBatis-学习笔记05【05.使用Mybatis完成CRUD】

    Java后端 学习路线 笔记汇总表[黑马程序员] MyBatis-学习笔记01[01.Mybatis课程介绍及环境搭建][day01] MyBatis-学习笔记02[02.Mybatis入门案例] M ...

  4. MyBatis:CRUD功能

    在前面已经自动生成了mapper和pojo,接下来我们实现mybatis的CRUD功能,先新建service.controller层的方法. 这里的sid是一个开源的id生成类,写完后,我们还需要在启 ...

  5. 2021年3月8日:MyBatis框架学习笔记02:利用MyBatis实现CRUD操作

    MyBatis框架学习笔记02:利用MyBatis实现CRUD操作 在第一节课中我们在UserMapper.xml里定义了两个查询语句:findById和findAll,对应的在UserMapper接 ...

  6. Mybatis实现CRUD

    1.Mybatis实现CRUD操作 1.CRUD是什么? CRUD:增加(Create).读取查询(Retrieve).更新(Update)和删除(Delete) 查询 查询所有数据 查询详情 条件查 ...

  7. 使用MyBatis实现CRUD操作

    MyBatis 1.使用MyBatis实现CRUD操作 1.1 根据id查询信息 1.2 实现存入用户信息 1.3 更新用户信息 1.4 删除用户信息 1.5 模糊查询 1.6 #{}和${}的区别 ...

  8. MyBatis和ORM的区别以及Mybatis和Hibernate的区别

    ORM: 即Object Relation Mapping,即对象关联映射.所以ORM就是将Java中的对象和数据库中的表关联对应起来. 思考一下Mybatis,Mybatis相当于将mapper中的 ...

  9. MyBatis基础-CRUD

    一.mybatis  环境搭建步骤 第一步:创建 maven 工程 第二步:导入坐标 第三步:编写必要代码(实体类和持久层接口) 第四步:编写 SqlMapConfig.xml 第五步:编写映射配置文 ...

最新文章

  1. c++引用matlab类,matlab调用C++函数浅谈(一)
  2. oracle form 头行合计,FORM主从块头行金额汇总
  3. 第十七章 大规模机器学习-机器学习老师板书-斯坦福吴恩达教授
  4. optee3.14 qemu_v8的环境搭建篇(ubuntu20.04)--镜像方式-直接使用
  5. sql网站路径php,如何在源码中找出sql语句的位置呢
  6. 移动端输入框弹出键盘控制
  7. php基础教程 第六步 学习数组以及条件判断switch补充
  8. 日期插件rolldate.js的使用
  9. 安装electron-react-boilerplate遇到的问题
  10. Android studio中添加外部的jar包
  11. Struts2学习笔记(十六) 文件上传(File Upload)
  12. Ubuntu+Docker+Tensorflow+GPU安装
  13. win64位系统注册表
  14. 开源公告|微信云端深度学习推理框架WeChat TFCC开源啦!
  15. Maya2011下载 (破解正式版)
  16. 获取其他APP中素材
  17. android 静态工厂方法,Android 源码中的静态工厂方法
  18. php rabbitmq报错重连,README.md · ahming/spring-websocket-example - Gitee.com
  19. 基于51单片机GPS的导航系统设计(2)---具体实施
  20. 总结:Prometheus存储

热门文章

  1. Java项目:前台预定+后台管理酒店管理系统(java+SSM+jsp+mysql+maven)
  2. php配置控制器和视图位置,视图控制器
  3. Js打印表格时部分边框不显示(table 标签)
  4. jQuery中的插件机制
  5. ffmpeg 常用命令
  6. vue注册新节点_vue怎么重新组装slots节点
  7. 1小时学会:最简单的iOS直播推流(三)使用系统接口捕获音视频数据
  8. iOS动画进阶 - 手摸手教你写ShineButton动画
  9. 常用的linux的命令行操作
  10. 201621123057 《Java程序设计》第12周学习总结