自从Edgar F. Codd在35年前提出数据可以被规范化为关联表格的集合,SQL已经出现了很长一段时间了.从那时起,整个IT业已经投入了数十亿美金在关联数据库管理系统(RDBMS)上了.没什么软件技术人员声称可以经受住时间,关系数据库,SQL的考验。实际上,在这个时候,在关系技术背后还有股巨大的推动力——世界上数家最大软件企业,这是关系技术发展的基石。所有的一切显示SQL将再风靡30年。
IBATIS的思想基础是关系数据库与SQL的价值。数据库甚至SQL比程序,甚至多个版本的程序生存的更久,这样的经验我们有很多。有时候,我们见过应用程序被用另一种语言重写,但数据库和SQL却没有大的变化。
所以IBATIS不打算隐藏SQL或者避免SQL。IBATIS是一个持久层框架,代替信奉SQL,它可以更简单的与SQL合作,并能更简单的与面向对象的软件结合。如今,有些流言说数据库和SQL会威胁我们的对象模型,但事实并非如此。IBATIS会让你确信这点。
自从
Edgar F. Codd在35年前提出数据可以被规范化为关联表格的集合,SQL已经出现了很长一段时间了.从那时起,整个IT业已经投入了数十亿美金在关联数据库管理系统(RDBMS)上了.没什么软件技术人员声称可以经受住时间,关系数据库,SQL的考验。实际上,在这个时候,在关系技术背后还有股巨大的推动力——世界上数家最大软件企业,这是关系技术发展的基石。所有的一切显示SQL将再风靡30年。
IBATIS的思想基础是关系数据库与SQL的价值。数据库甚至SQL比程序,甚至多个版本的程序生存的更久,这样的经验我们有很多。有时候,我们见过应用程序被用另一种语言重写,但数据库和SQL却没有大的变化。
所以IBATIS不打算隐藏SQL或者避免SQL。IBATIS是一个持久层框架,代替信奉SQL,它可以更简单的与SQL合作,并能更简单的与面向对象的软件结合。如今,有些流言说数据库和SQL会威胁我们的对象模型,但事实并非如此。IBATIS会让你确信这点。
在这一章,我们将看到IBATIS的历史和基本原理,并且讨论它对于基于它的产品的影响力。
1.1 一种混合的解决方案:包括最好的
在当今世界,混合方案到处都是。接受两种表面上相反的观点,融合他们被证明是一种制造小市场的有效方法,有时甚至能创造整个行业。在汽车行业,这是绝对正确的,汽车上大部分的设计都是来源于混合了各种想法的设计。(略去作者举例N字…)
混合解决方案在IT行业依旧被证明是正确的.IBAITS就是这样一个持久层解决方案.随着时间的流逝,各种让应用程序在数据库中执行SQL的方法被开发出来.IBATIS是一种特别的解决方案,从多种分支方案中借用了一些理念.让我们简单的看下这些分支吧.
1.1.1 游览IBATIS的基础
IBATIS从当今最流行的访问关系数据库方法中获得构思和品质,并寻找让他们合作的方法. 图1.1显示IBATIS这几年从不同途径集成数据库的开发中吸取的经验,以此开发了一个混合的方案.
下面的片段讨论多种和数据库的交互方法,并讲述IBATIS学习的部分.
SQL
SQL是IBATIS的心脏.所有的关系数据库将SQL作为最基础的交互方法.SQL是一种简单,非程序化的语言.实际上,SQL包括2种语言.
第一种是数据定义语言(DDL),包括CREATE,DROP和ALTER. 用来定义结构和设计数据库,包括表,列,索引,常量,过程,外键等.IBATIS并不直接支持DDL,虽然很多人成功的使用IBATIS执行了DDL.DDL通常由数据库管理员操作,而不是程序开发者.
图1.1
第二种是数据操作语言(DML).包括SELECT,INSERT,UPDATE,DELETE,用来直接操作数据.基础的SQL被设计成足够简单,以至于终端用户也可以使用,不需要提供一个丰富的用户接口或者一个应用程序来操作它.不过在绿屏年代,这是相反的,用户总想能做更多.(绿屏年代...好冷)
如今,数据库已经太复杂到无法由终端用户直接操作SQL.你能想象递给你的会计处一堆SQL语句,就象在说:"去吧,你可以在B表中找到你需要的信息."
单独SQL已经不再是个终端用户的高效接口,不过对于开发人员来说,SQL是一个非常强大的工具.SQL是唯一的完全数据库访问方式,其他的所有方法都只是它的子集.因此,IBATIS将SQL作为访问关系数据库的基础方法.同时IBATIS也提供许多其他`在本章讨论的访问方法的优点,包括存储过程和对象/关系映射(ORM)工具.
早期的存储过程
存储过程可能是最老的关系数据库应用程序.许多遗留下来的应用程序使用一种现在叫双重(two-tier)的设计.双重设计涉及一个富客户端直接调用数据库里的存储过程,存储过程包含需要在数据库里执行的SQL.除SQL外,存储过程可以而且常常包含业务逻辑.不象SQL,存储过程语言是程序化的,而且有流控制,比如条件和迭代.其实,可以写一整个应用程序仅仅只使用存储过程.许多软件供应商开发富客户端工具来开发双重数据库应用程序,比如Oracle Forms,PowerBuilder,Visual Basic.
双重应用程序的首要问题是性能和扩展性.虽然数据库是非常强大的机器,但它们不是处理成千上万个用户的最好选择.伴随着现代的网络应用程序,这些扩展性的需求是经常的.并行许可证,硬件资源甚至网络端口等限制,阻碍了这样的架构在大型系统方面的成功.另外,开发双重应用程序简直是噩梦.除了通常的富客户端部署部件,经常还要部署复杂的运行时数据库引擎.
现代的存储过程
某些情况,在三重和N重应用程序中存储过程仍然是最好的解决方法,比如网络应用程序.存储过程如今被使用得更象远程调用,性能问题也被连接池和数据库资源管理所缓解.存储过程仍然是一个可选的设计方案用来在面向对象应用程序中实现完整的数据访问层.存储过程的优点在于能比其他方案更快的控制数据库里的数据.当然,还要考虑其他问题不仅仅是性能.
将业务逻辑混合在存储过程中,被广泛的认为是很差的方式.最基本的原因就是存储过程在现代的软件架构中较难开发,难以编写,测试和部署.更糟的是,数据库常常由另一个团队管理,而且被紧密变化控制(tight change control不知道咋翻=.=)保护着.往往不能随着现代软件开发理论要求的那样快速变化.而且,因为其可扩展性,存储过程也不能完整表达业务逻辑.如果业务逻辑涉及其他系统,资源或者用户接口等,存储过程往往不能表达所有的业务逻辑.现代应用系统是非常复杂的,而且需要一种普通的语言来优化数据操作.为了处理这些,一些供应商将更强大的语言比如JAVA集成进数据库引擎来提供更强大的存储过程.不过,这个并没有解决问题,仅仅导致了数据库和应用程序界限间的混淆,还加重了数据库管理员的担子:如今他们还要担心数据库中的C#,JAVA.这实在是个糟糕的工具.
过校正(overcorrection)是软件开发中一个经常的主题.当一个问题出现时,第一个解决方案往往是完全相反的路线.结果往往是同等难题,而不是解决方案.这引导我们进行内联SQL的讨论...
内联SQL
一种解决存储过程限制的方法是将SQL嵌入其他普通化的语言,而不是将应用程序的业务逻辑转移到数据库.这使得SQL可以与普通语言交互.在某种意义上,SQL成为了该语言的一个特性.这种方法已经被很多语言实现了,比如COBOL,C,JAVA.下面是JAVA中的SQLJ的例子:
String name;
Date hiredate;
#sql {
SELECT emp_name, hire_date
INTO :name, :hiredate
FROM employee
WHERE emp_num = 28959
};
由于内联SQL与语言的紧密结合,所以它是优雅的.语言中的变量可以被直接传给SQL,或者SQL的运行结果可以被直接传给语言中的变量.在某种意义上,SQL成为了该语言的一个特性.
不幸的是,因为不同的版本的一些特性,使内联SQL没有广泛的推广.第一,SQL不是一个标准.这些SQL语言碎片使得很难去创造一个内联SQL翻译器可以在不同的数据库平台上完整的移植.第二,SQL很少被作为一个语言真正的特性来实现.而是由预编译器先将内联SQL编译成语言能接受的代码.这带来一些问题,比如IDE(开发环境)为了提供一些高级功能比如语法高亮,代码自动完成等,必须先解释代码.而带内联SQL的代码可能无法在没有预编译器的情况下完成编译.
一种解决方案就是将SQL从语言级别移走,成为应用程序中的一种数据结构(比如,String).这种技术现在被成为动态SQL.
动态SQL
动态SQL通过免去了预编译器来解决了内联SQL的部分问题.替代的,SQL被表现为能够象现代语言中普通字符数据一样操作的字符串.因为SQL被表现为string,所以它不能象内联SQL一样与普通语言直接互操作.因此,动态SQL的实现需要有API来设置SQL参数或者获取结果数据.
动态SQL具有高适应性.SQL可以在运行时通过不同的参数和动态应用程序函数进行操作.比如,一个查询网页会允许用户选择查询的字段和数据.这就需要动态的更改Where子句,通过动态SQL这能够很容易做到.
在现代语言里,动态SQL是现在最流行的访问关系数据库的方式.大多数语言都包含一组API用来访问数据库.JAVA和.NET开发人员都会熟悉各自语言里的这些标准API:JDBC和ADO.NET.这些标准API一般都非常强大且为开发人员提供了很强的适应性.接下来是一个JAVA中动态SQL的例子:
String name;
Date hiredate;
String sql = "SELECT emp_name, hire_date"
+ " FROM employee WHERE emp_num = ? ";
Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement (sql);
ps.setInt (1, 28959);
ResultSet rs = ps.executeQuery();
while (rs.next) {
name = rs.getString("emp_name");
hiredate = rs.getDate("hire_date");
}
rs.close();
conn.close();
不用怀疑,动态SQL并没有内联SQL般优雅,甚至不如存储过程(甚至我们可以忽略Exception处理).这些API往往很复杂,而且冗长,就象刚才的例子.使用这些框架经常会导致大量的重复代码.而且,SQL也经常会很长而无法写在一行中.这意味着string不得不被截成数个string.连接起来的SQL代码会导致难以阅读和操作.
既然存储过程,内联SQL,或者应用程序中的数据结构都不是处理SQL的最好方法.那我们应该怎么做呢?我们避免它.在现代面向对象程序中,一个最引人注目的与关系数据库交互的方法就是使用ORM工具.
对象/关系映射 ORM
ORM被设计通过将SQL从开发人员的职责中移除来简化对象的持久化工作.有些工具在编译的时候,有些则在运行时,自动的创建SQL.SQL根据类和关系数据库中的表之间的映射创建SQL.除了移除了SQL,开发人员使用ORM相关的API也比使用标准的SQL API来得简单得多.ORM其实是个几乎和面向对象编程语言一样古老的概念.这些年,ORM技术的许多发展使得它成为持久化方案中引人注目的一个分支.
现代ORM工具不仅仅生成SQL而已.它们提供了一整套对整个应用程序有益的持久化架构.任何优秀的ORM工具都会提供事务管理,包括简单的API用来处理本地或者分布式事务.ORM工具往往还提供多层的缓存策略来处理不同类型的数据,以避免不必要的数据库联系.ORM减少数据库交流另外一个方法是延迟读取(lazy loading).延迟读取就是只有当数据真正需要的时候才被读取.
但是,并不意味着ORM工具适合任何情况.ORM工具的基础是假设和规则.最普遍的假设就是数据库都是标准化的.如我们将在1.4节里讨论的,最大型而且极具价值的数据库很少被完美的标准化过.这会是映射变得复杂,或造成设计中的低效.没有ORM方案能够提供对所有数据库的特性,功能和设计缺陷的支持.如前所述,SQL并不是可靠的标准,所以每个ORM工具只能提供所有数据库的部分功能.