MyBatis For .NET学习笔记[3]:工作原理解析

上篇中说道 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 原著<<IBatis in Action>> PDF 文档 [DownLoad Link] 作者正好是 IBatis 开源项目发起人 [CLINTON BEGIN]/[BRANDON GOODIN]./[LARRY MEADORS]. PDF Document 中 IBatis 团队在 How It Work? 章节中详细阐述 IBatis 工作原理. 这时目前为止找到官方最为详细的回应. 如下结合 BUI 项目将部分翻译其中片段.

<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 层次之间关系:

2011-04-07_132710

 

 

 

 

 

 

 

 

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 之间. 也就是说它将数据库的元数据映射到类的元数据. 数据库表每个列都与实体类属性字段建立映射.

2011-04-07_134930

 

 

 

 

 

 

 

如上看到 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 语句:

2011-04-07_142717

 

 

 

 

 

 

当执行一条查询语句时查询数据作为 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