上篇中说道MyBatis的前身是IBatis演化而来.其实IBatis发展到今天已经有9年的历史.当然目前最新版本是IBatis 3.0发布于2009
年7月. 分别演化出不同的Java和.NET版本. 从3.0发布至今为止尚没有做下一个版本的更新.官方也并没提出下一个版本的更新计划.但这并不妨碍我们去进一步去探究MyBatis For .NET内部工作的原理.
其实在写这篇MyBatis原理时 刚开始的思路想从MyBatis插入一条记录过程来探讨MyBatis是如何解析SQL语句并执行一个Entity Object Mapping 到DataTale过程. 下午和一个同事做了一下交流.他提出一个看法是"你这样做还是教别人如何去用MyBatis的API.那只是MyBatis定义的一套规则而不是真正原理." 这句话仿佛提醒了我换一个角度来对MyBatis在设计Mapping时原理重新解析.我们应该从MyBatis实现整个Mapping过程细节泥沼之中跳出来.专注更多细节只会让我们离MyBatis设计初衷背离的越远.
后来在Code Of Google获得一本推荐IBatis原著<
<1>What exactly is MyBATIS?
上篇打该从CRUD的操作学会如何简单使用IBatis框架.可以看到它Mapping工作中核心的对象对应关系不是EntityModel实体对应关系型数据库中DataTable 而是EntityModel实体隐射SQL Statement.在来回归到What Exactly IS MyBatis?这个问题上.我们先把这些细节撇开来看一下设计MyBatis的初衷.
MyBatis作为一个独立的ORM框架.当然它的核心就是DataMApper数据关系之间的映射.当然针对映射层Martin Fowler大叔在他的《Patterns of Enterprise Application Architecture》一书中针对Data Mapper做了一次经典的描述:
一个映射层,在对象和数据库间传递数据,并保持两者与映射层本身相独立.
做一张图来说明DataMapper层次之间关系:
Martin Fowler大叔阐述提到两个特点:
[1]DataMapper Layer是相对Relation DataBase和Entity Model Layer是独立的
[2]DataMapper主要职能是实现Relation DataBase和Entity Model 之间数据交互.
谈到ORM工具.不得不提的是Nhibernate.但是这里有两个概念 数据映射[Data Mapping]和元数据映射[Metadata Mapping].而元数据映射恰恰体现ORM实现的根本依据. 这在Nhibernate中体现很明显.例如Nhibernate中定义一个EntityModel-Customer类时实现隐射的关联方式常常是建立指定具体的Customer类字段与对应数据库DataTAble表Fild之间.也就是说它将数据库的元数据映射到类的元数据.数据库表每个列都与实体类属性字段建立映射.
如上看到Nhibernate框架所采取的映射方式.而MyBatis则不同.它不是直接在类与数据表或字段与列之间进行关联,而是把SQLStatement 语句的参数[parameter]和返回结果[result]映射至类.其实从图1就可以看出MyBatis处于数据库表与实体对象层之间的中间层. 当我们需要修改时大部分工作集中MyBatis中来.而无需修改数据库表结构和实体对象结构.这种松散耦合.SQL 其实这种核心还是建立在SQL Statement语句来分离数据库表结构和实体对象之间设计.
那我们要做什么呢?
我们只需编写对应Action操作的对应的SQl 语句, MyBatis的工作则是替代我们去解决数据库表结构与实体对象之间的映射.其实这种方式在MyBatis团队内部把DataMapper直观叫做SQL Mapper.
到这里你大概明白MyBatis的底层是在做什么工作了.
<2>SQl Mapper
到这里大概预览MyBatis底层工作原理.来进一步解析SQL Mapper的核心.MyBatis团队当初在设计这个框架.巧妙采用SQL Statument作为灵活映射载体.任何一个SQL语句都可以看做是数据库的流入和流出. 输入的值作为参数[Parameter] 通常出现And/Where条件子句中. 输出的数据则一般体现Select中指定表字段.列 举一个例子吧 一条SQL语句:
当执行一条查询语句时查询数据作为OutPut输出结果 ,条件后则作为输入参数Parameter.这种新的角度来看SQL 语句是在我们单一使用时所看不到的.一条平常SQL语句给人耳目一新感觉.这也是MyBatis在设计 一个独到匠心之处. 这又给我们带来什么呢?
作为独立中间层SQl Mapper为Developer提供足够的灵活性.而这些相对Nhibernater一站式封装不可见的底层来说.为DVP开放一个可以灵活操作的窗口.可以在不修改数据库表结构的前提下轻松地操作数据使之与对象EntityModel模型进行匹配. 同时不要忘了SQl语句本身所带来的强大语法支持.直接可以使用内置的数据库函数或存储过程来返回多个不同的表或结果. 这种直观的操作不得不说对DVP来说在很具有诱惑性.
<3>SQL Mapper and ADO.NET
在没有提出ORM这个概念之前.如果要做一次数据访问.MS给我们API中关联几个对象SQLConnection、SQlCommand./SQlDataReader等都是不可或缺的.以前我记得还在使用VS2005是Petshop中也能看到SQLHelper的身影.甚至每个人对SQLHelper需要都不一样.在其中添加访问数据库功能和扩展也不尽相同.
不知各位是否还记得在02年MS推出C#和.NEt层以PetShop为蓝本声称以28倍的性能优势和1/4的代码量领先于Java.遭到Java社区口水战,其实这里我要说什么呢?JAVA在数据访问如果以JDBC方式也是有一个固定重复的代码. 对于高质量 业务需求下.不同语言数据访问代码重复编写必然会降低开发人员的效率.这时代码生成器应运而生. 在一定程度上把DVP从重复数据访问工作解放出来. 随着业务需要这种解放依然不够灵活彻底.而MyBatis则完全可以替代ADO.NET.
类似利用MyBatis插入一条记录 要做什么 首先配置Insert时Action执行的SQL语句:
1: <statements>
2: <Insert id="InsertCustomer" parameterClass="Customer">
3: INSERT INTO dbo.Customer
4: ( Customer_Name ,
5: Customer_Sex ,
6: Customer_Age ,
7: Customer_Address ,
8: SignOn_Data
9: )
10: VALUES ( #CustomerName# , #CustomerSex# , #CustomerAge# , #CustomerAddress#,#SignDate# )
11: Insert>
12: <statements>
13:
配置完成后执行获取到Customer数据只需简洁一句:
1: public int InsertCustomerByTest(Customer getCustomer)
2: {
3: //Insert Customer
4: object getresult= Mapper.Instance().Insert("InsertCustomer", getCustomer);
5: return Convert.ToInt32(getresult.ToString());
6: }
这样一条Cusotmer数据成功插入.相对ADO.NET那套繁琐访问数据库过程.MyBatis这种方式你会发现代码的量显著减少 但是效果是一样的.MyiBATIS的代码要简练得多 而且也更容易维护.只需你做好SQL语句映射配置.其他相关"隐藏"资源管理工作则有MyBatis取代了.就想Batis团队Blog中一句话:"just care what do you want " 只需关注你所关心的.或是说在这套框架约束下你需要持续维护即可.类似映射关系.以极小代价换取同样的效果.
<>How To USe MyBatis?
其实每一种框架都建立于规则和约束之上的.而对于低层次的框架类似ADO.NET 虽然提供灵活 完整的API使用. 却难以使用.但是我们却无法忽视这种低层次框架所适用范围. 而较高层次的框架如O/RM工具非常易用,剔除了重复代码工作量.但我们不能忘了往往这种自动化是建立更多的假设和约束之上.这也导致有些ORM框架不能完整适用更多的应用程序.这也就某种程度上照成有些ORM框架在解决一些复杂棘手问题之后同样也留下一切不可逾越的缺陷.当了解一个ORM框架原理后 也应该客观正视它所具有的优缺点.在合适场景使用才能发挥类似MyBatis ORM框架所带来高效.
so How To Use Mybatis?先了解MyBatis具有优缺点.
MyBatis框架高于ADO.NET 却没有Nhibernater一站式封装. MyBatis更多体现的是"半自动化"灵活性 这样就导致Mybatis就具有自己使用范围.
[1]不适用SQL的动态生成
MyBatis突出体现中一点就是SQL语句得开发人员自己编写. 这也是体现MyBAtis”半自动”这个核心特点. 这也是区别Nhibernater顶层对SQL动态生成一个很重要因素.但这也给MyBatis带来一定限制.
如果应用程序中设计核心SQL是动态生成的.这个时候你就发现MyBatis的表现就极为低效.MyiBATIS有着很强大的动态SQL特性,支持高级查询,甚至是一些动态的更新功能。但如果程序中的每条语句都是动态生成的,那么最好还是使用原生的ADO.NET,或者构建自己的框架。MyiBATIS的强大很大程度上体现在允许开发人员自由地手工编写SQL,直接操作SQL。如果大部分SQL都是动态生成的,那么这个优势无疑就丧失掉了
[2]提倡关系型数据库
其实昨天我还看到MyBatis官方论坛有的DVP把MyBatis使用以原始文件为基础的数据管理上 类似XML数据文件.中使用MyBatis框架.当然也社区中反馈不少使用相关问题.从官方团队反馈翻译过来:MyiBATIS不会对环境做出很多假设。但它仍然希望您使用的是一款真正的关系型数据库,同时支持事务和相对标准的SQL及存储过程。即使是一些知名的数据库也会有不支持关系型数据库关键特性的情况。MySQL的早期版本不支持事务,因此MyiBATIS用起来就不太好.官方明确在3.0提示建议使用关系型数据库.
[3]MyBatis的性能
性能这个在ORM框架也是一个很重要的因素.但是从一开始我并不对MyBatis的性能有过多的担心.毕竟把执行SQL权利交给程序员自己.这在很大程度上未性能留有余步. 其实昨天我看到IBM上对MyBatis原理类库结构大概用Refector看了一下核心的代码.其实在映射处理上采用.NEt的反射机制来实现匹配.任何框架都会存在或多或少的性能损失。一般地,比较一下手工编写的ADO.NET和MyiBATIS,在一个for循环中遍历一百万次,会发现ADO.NET更具优势。幸运的是,在当今应用程序开发中,这并不是关键的性能点。更为重要的是,如何从数据库中获取数据,何时获取它,以及获取的频率。例如,从数据库中获取分页后的列表数据能显著地提升应用程序的性能,因为这样就避免了一次加载过多的数据。类似的,使用像延迟加载[lazy load]这样的特性可以避免在给定的用例下加载不必要的数据。另一方面,如果我们确定需要加载复杂的对象属性,而这些属性来自于多个数据表,那么使用单条SQL来加载数据也可以极大地改善性能。iBATIS提供了多种性能优化策略.性能删MyBatis完全能满足大部分需求.
当然如上只是提到个人认为需要注意的地方.MyBatis本身是办ORM 但是依然具有ORM框架的提高生产力,易用,架构上支持分层的特点等.
参考资料:
深入分析IBatis框架系统架构和隐射原理
Wiki MyBatis Detail
camel.apache.org[Mybatis]
introduction-to-ibatis-mybatis-an-alternative-to-hibernate-and-jdbc