今年,我有机会在TechEd见到了泰德·纽瓦德(Ted Neward)。泰德在2004年末创造了著名的短语“对象关系映射是我们行业的越南”。
这是一个可怕的类比,但很贴切。我看到开发人员多年来一直在为关系数据库模型和传统对象模型之间的巨大不匹配而苦苦挣扎。而他们提出的所有解决方案似乎都让问题变得更糟。我完全同意Ted的观点;对象/关系映射问题没有很好的解决方案。当然,解决方案是有的,但它们都涉及到严重而痛苦的权衡。最糟糕的是,通常要到开发周期的后期才能看到这些权衡的结果。
Ted发布了一篇备受期待的博客文章,详细分析了ORM问题。这是一篇很长的帖子。但是,除非您是ORM战争中饱受战争创伤的老兵,否则我强烈建议您至少浏览一下,这样您就会意识到尝试实现ORM解决方案时可能遇到的许多陷阱。外面有很多神奇的子弹,不乏天真的开发人员。
泰德的帖子写得很好,也很权威,但有点冗长;我觉得自己在读这篇文章的时候,就像是在体验越南的一小部分。让我们直接跳到结尾处的总结,它提供了ORM问题的当前(和未来)解决方案的一个很好的列表:
遗弃。开发人员干脆完全放弃对象,返回到不会产生对象/关系阻抗不匹配的编程模型。虽然令人不快,但在某些情况下,面向对象的方法会产生比节省的开销更多的开销,而且ROI根本无法证明创建富领域模型的成本是合理的。([福勒]对此进行了一定深度的讨论。)。这相当巧妙地消除了问题,因为如果没有对象,就不会有阻抗不匹配。
全心全意的接受。开发人员干脆完全放弃关系存储,而使用一种适合他们选择的语言看待世界的方式的存储模型。对象存储系统(如db4o项目)通过将对象直接存储到磁盘来巧妙地解决了问题,消除了许多(但不是所有)上述问题;例如,没有第二个模式,因为使用的唯一模式是对象定义本身的模式。虽然许多DBA一想到这一点就会昏昏欲睡,但在一个日益面向服务的世界里,它避开了直接数据访问的概念,而是要求所有访问都通过服务网关,从而封装了存储机制,使其不会被窥探,想象开发人员以一种对他们来说更容易使用的形式存储数据,而不是DBA,这是完全可行的。
手动映射。开发人员简单地承认这毕竟不是一个很难手动解决的问题,并编写直接的关系访问代码来将关系返回给语言、访问元组并根据需要填充对象。在许多情况下,此代码甚至可能由检查数据库元数据的工具自动生成,从而消除了对此方法的一些主要批评(也就是说,编写和维护的代码太多了)。
接受ORM限制。开发人员简单地接受这样一个事实,即没有办法高效、轻松地结束O/R不匹配的循环,并使用ORM解决80%(或50%或95%,或任何看起来合适的百分比)的问题,并利用SQL和基于关系的访问(例如,RAW&34;JDBC或ADO.NET)将它们带过ORM会产生问题的区域。然而,这样做本身也有一定的风险,因为使用ORM的开发人员必须意识到ORM解决方案在其中进行的任何缓存,因为关系访问显然无法利用该缓存层。
将关系概念集成到语言中。开发人员简单地接受这是一个应该由语言解决的问题,而不是通过库或框架。在过去的十年或更长时间里,O/R问题的解决方案的重点一直集中在试图使对象更接近数据库,这样开发人员就可以专门专注于单一范例(当然,该范例就是对象)中的编程。然而,在过去的几年里,人们对具有更强的集合和列表支持的脚本语言(如Ruby)的兴趣引发了这样的想法:也许另一种解决方案是合适的:将关系概念(本质上是基于集合的)引入主流编程语言,从而更容易弥合集合和对象之间的差距。到目前为止,这一领域的工作还很有限,主要局限于研究项目和/或边缘语言,但一些有趣的努力正在社区中获得知名度,例如函数/对象混合语言(如Scala或F#),以及直接集成到传统面向对象语言(如Microsoft针对C#和Visual Basic的LINQ项目)。不幸的是,SQL/J策略失败了;即使在那里,该方法也是有限的,不寻求将集合合并到Java中,而只是允许转换程序对嵌入式SQL调用进行预处理并将其转换为JDBC代码。
将关系概念集成到框架中。开发人员简单地接受这个问题是可以解决的,但前提是要改变观点。开发人员不再依赖语言或库设计器来解决这个问题,而是以不同的视角看待对象,即本质上更具关系性,构建更直接地围绕关系构造构建的域框架。例如,开发人员不是创建将其实例数据直接保存在对象内的字段中的Person类,而是创建将其实例数据保存在RowSet(Java)或DataSet(C#)实例中的Person类,这些实例可以与其他RowSet/DataSet组合成易于传送的数据块,以便根据数据库进行更新,也可以从数据库解包到各个对象中。
泰德很快发布了一个后续条目,回应了人们对他最初帖子的普遍批评。如果你现在用鼠标左手放在评论链接上,你可能想先读一读。
就我个人而言,我认为ORM问题的唯一可行解决方案是选择其中一个:要么放弃关系数据库,要么放弃对象。如果从方程式中去掉O或R,就不再有映射问题。
放弃传统的Customer对象--或者放弃传统的Customer表--可能看起来很疯狂,但是选择其中一个是完全明智的选择,而不是ORM解决方案通常会给我们留下的复杂的类、对象、代码生成、SQL和存储过程的泥潭。
这两种方法当然都是有效的。我倾向于站在数据库即模型阵营的一边犯错误,因为我认为对象被高估了。