iBatis.Net(5):Data Map
总算,总算,能写点示例啦,呵呵,其实前面的几篇,我感觉自己写的也很生硬,没有Demo理解起来是很困难,很多名词,反正我初次接触iBatis的时候,是一点也不理解的,我也是比较习惯与从示例中学习的,到今天这一篇,终于可以完成一个可以让iBatis真正运行起来的实例啦,前面的,可能只能理解为纸上谈兵,不过之所以这么设计,也是不得意而为,因为你不从本质上了解它的协作过程,不会把这个框架运用的很好的
这一篇要写到的,就是我们所有的查询语句的定义,DataMap,它也是定义在xml文档里的,的确iBatis.Net没有像nhibernate和linq to sql那样,不需要写sql语句,不过也正是因为在这里我们还是需要写sql语句,才更好的说明了iBatis.Net的特性,或者说叫优势,这里说这些可能您还不懂得这个优势到底体现在哪里,看完这篇文章,我觉得您一定会觉得略微猥琐的爱上它的,哦,突然想起来,有朋友在我上一篇的留言里说,让我把iBatis.Net和nhibernate对比的写一下,我如果有时间,会写一些关于nhibernate的文章的,但是,我还是觉得,您如果真想了解这两个框架的优劣势,还是先把这两个框架都研究个差不多,然后自己去体会他们的差距和优劣势比较好,我可能对某一种框架的某种特性有特别的偏爱,而可能您却不以为然或者您一点也不这么觉得这个特性会带来多少的方便,所以,差距在哪,您还是需要身体力行的自己去观察,去体会。。。。好了 闲话少说,书归正传吧
这一节的所有实例使用到的数据库,还是我们经典的NorthWind,如果您还没有这个示例的数据库的话,我会在这篇文章结束后添加一个下载,包括我这一节所有的示例源码
首先为了可以使用到一些常用的配置项,我们需要新建一个项目,专门用来存放数据实体类,当然这是一个很不好的做法,但是,这只是个示例,您在实际操作的时候千万别这么做,我推荐的做法是,把对于一个使用到当前实体类的所有操作代码,以方法的形式分解开,然后全部放在这个实体类里,这样可以有效的避免耦合
iBatisSample是我创建的一个c#控制台应用程序,Domain是我创建的一个类库项目,在这个项目里创建一个Customers类,我想我不说您也可以知道它的内容啦,不过我还是把这个类和我们需要映射到的数据表结果展示一下:
Customers类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Domain { public class Customers { public string CustomerID { get; set; } public string CompanyName { get; set; } public string ContactName { get; set; } public string ContactTitle { get; set; } public string Address { get; set; } public string City { get; set; } public string Region { get; set; } public string PostalCode { get; set; } public string Country { get; set; } public string Phone { get; set; } public string Fax { get; set; } } }
数据库中的Customers表结构:
当然,我这里为了方便,就使用了相同的字段名称,当然您完全可以不这么做,包括类的名称也是,继续看下去就明白了,
我们创建一个叫做Test.xml的文档
<?xml version="1.0" encoding="utf-8" ?> <sqlMap namespace="its" xmlns="http://ibatis.apache.org/mapping" xmlns:xls="http://www.w3.org/2001/XMLSchema-instance"> <statements> <select id="SelectAllCustomers" resultClass="Domain.Customers"> Select * from Customers </select> </statements> </sqlMap>
这里我有生以来见过的最简单的iBatis示例啦,先不对它做任何解释,如果我们要使用它,还需要在SqlMap.config中添加对它的引用
<sqlMaps>
<sqlMap resource="http://www.cnblogs.com/Maps/Test.xml"/>
</sqlMaps>
这样,我们就可以在我们的程序代码里这样使用它
ISqlMapper mapper = Mapper.Instance(); IList<Customers> customers = mapper.QueryForList<Customers>("SelectAllCustomers", null); foreach (Customers customer in customers) { Console.WriteLine(customer.CustomerID+" | "+customer.CompanyName); } Console.ReadKey();
运行,如果你的各种配置没有错的话,那么就可以输出整个Customers表里的数据啦,当然,这种第一次成功的的情况太少了,原因之一也是因为iBatis.Net的这个错误的机制还是有点问题的,实话说,我在第一次运行iBatis程序到得到结果之间的调试绝对超过半个小时,这也是我一直强调,不要着急示例,一定要充分的了解它再使用它的一个原因
然后,我再解释一下整个程序(虽然破烂不堪)的运行过程,首先通过Mapper.Instance()获取DataMapper实例,然后通过DataMapper提供的API QueryForList<Customers>()方法,得到一个泛型化的列表,因为我们在映射文件中定义的是Customers表中所有的数据,然后通过这个Select节点的resultClass定义返回数据的实体类型,就是我们一开始定义的那个Customers列,因为这个类的属性都是与数据表中的列名和数据类型对应的,所以可以正常的实例化一个个的Customers实例,并且添加到列表中
但是,这里有个问题,在我们的映射文件中使用的resultClass为一个比较长的字符串,它实际上是一个类的完全限定名,虽然这个长度我们还暂时可以接受,但是如果像DataAccess.SqlServer.NorthWind.Domain.Customers这样长的名称,难道您还真打算一次一次的敲碎键盘为止嘛,您可能说可以复制粘贴啊,但是如果我们在复制粘贴了200份后,因为某些原因,这个类的位置有改动,您又将作何感想,所以,这里就需要使用到一个指定别名的方式,对我们上面的Test.xml做一下修改
<?xml version="1.0" encoding="utf-8" ?> <sqlMap namespace="its" xmlns="http://ibatis.apache.org/mapping" xmlns:xls="http://www.w3.org/2001/XMLSchema-instance"> <alias> <typeAlias alias="Customers" type="Domain.Customers"/> </alias> <statements> <select id="SelectAllCustomers" resultClass="Customers"> Select * from Customers </select> </statements> </sqlMap>
聪明的您一定看到的效果,我就不解释了,这个方式很好的解决了我上面的“***难”
但是,小白总是***难不断的人,现在又有了一个问题,如果我们的数据实体类已经定义好了,并且已经在很多地方使用,这个时候更改它是绝对不可能的啦,但是我们数据库中的列名与数据实体类中的属性名称却不对应,这个时候,我们就需要借助一个resultMap的东西,我们把上面定义的Customers中的属性名CustomerID改为CustomerIdent,我们修改Test.xml映射文件
<?xml version="1.0" encoding="utf-8" ?> <sqlMap namespace="its" xmlns="http://ibatis.apache.org/mapping" xmlns:xls="http://www.w3.org/2001/XMLSchema-instance"> <alias> <typeAlias alias="Customers" type="Domain.Customers"/> </alias> <resultMaps> <resultMap id="Customer" class="Customers"> <result property="CustomerIdent" column="CustomerID"/> <result property="CompanyName" column="CompanyName"/> <result property="ContactName" column="ContactName"/> <result property="ContactTitle" column="ContactTitle"/> <result property="Address" column="Address"/> <result property="City" column="City"/> <result property="Region" column="Region"/> <result property="PostalCode" column="PostalCode"/> <result property="Country" column="Country"/> <result property="Phone" column="Phone"/> <result property="Fax" column="Fax"/> </resultMap> </resultMaps> <statements> <select id="SelectAllCustomers" resultMap="Customer"> Select * from Customers </select> </statements> </sqlMap>
在修改过的映射文件中,我们没有使用resultClass直接指定一个实体类,而是指定了一个我们定义的名为Customer的resultMap,在这个resultMap中,我们定义了所有返回列和所有数据实体类中属性的映射关系,这样就解决了我们上面的问题
那么,这是查询,插入、更新、和删除呢,我们应该怎么做?
插入
<insert id="InsertOneCustomer" parameterClass="Customers"> Insert into Customers (CustomerID,CompanyName,ContactName) values (#CustomerIdent#,#CompanyName#,#ContactName#) </insert>
更新
<update id="UpdateOneCustomer" parameterClass="Customers"> Update Customers set CompanyName=#CompanyName# where CustomerID=#CustomerIdent# </update>
删除
<delete id="DeleteOneCustomer" parameterClass="Customers"> Delete from Customers where CustomerID=#CustomerIdent# </delete>
这一篇先写这么多,关键是让您可以有一个基础的认识,当然,DataMap的内容远远不止这些,其实到现在,也没有真正的让大家感觉到它的优势在那里,只是让大家体验一下它整个的协作过程和一些基础的定义该怎么写,下一篇,我将会详细的介绍映射文件的内容和一些技巧性的东西,如果有可能的话,我想最晚不会超过今天,也希望对iBatis有兴趣的朋友继续关注吧