[转]为何我说ORM是一种反模式

原文地址:http://www.nowamagic.net/librarys/veda/detail/2217web

 

上周我在在上讨论了ORM,在那之后有人但愿我澄清个人意思。事实上,我曾经写文章讨论过ORM, 但那是在一场关于SQL的大讨论的上下文中,我不该该把这将两件事情混为一谈。 所以,在本文中我将关注ORM自己。同时,我尽力保持简略,由于从个人SQL文章中显而易见的是:人们倾向于一旦读到让他们发怒的内容就会离开(同时留下一句留言,而不论他们所关注的东西是否在后面会讨论到)。数据库

什么是反模式?

我很高兴地发现Wikipedia有一个至关全面的关于反模式的列表,包括来自编程界及其以外的内容。我之因此称ORM为反模式的缘由是由于,反模式的做者定义了用来区分反模式和普通的坏习惯的两个条件,而ORM彻底符合这些条件:编程

  1. 它开始的时候看起来颇有用,可是从长期来看,坏处要大过好处
  2. 存在已验证而且可重复的替代方案

因为第一个因素致使了ORM使人抓狂(对我来讲)的流行性:它第一眼看上去像是个好主意,可是当问题更加明显的时候,已经很难离开了。后端

这对ORM来讲是什么意思?

我想说的主要问题在于 ActiveRecord,它因为 Ruby on Rails 而著名, 从那之后已经移植到了许多其余语言。然而,这些问题一样存在于其余的ORM层,好比Java的Hibernate和PHP的Doctrine。函数

ORM的优势性能

  • 简单:一些ORM层告诉你它们“消除了对SQL的要求”。我至今仍然看到这种承诺在传播。其余一些会更加现实地声称它们能够减小手写SQL的须要,可是仍然容许你在须要的时候使用它。对于简单的模型以及项目的早期,这确实是一个优势:使用ORM,无疑你可以更快地开始启动。然而,你将会走向错误的方向。
  • 代码生成:使用ORM从模型中消除用户层面的代码,这一作法开启了通向代码生成的大门。经过对schema的简单描述,“脚手架”模式能够为你的全部表生成一个可工做的界面。更加具备魔力的是,你能够修改你的schema描述,而后从新生成代码,从而消除了CRUD。一样,这在开始的时候确实是可行的。
  • 性能“足够好”:我没有看到任何ORM层声称在性能上更加优越。很明显,为了代码的敏捷性须要付出性能的代码。若是哪里变慢了,你老是能够用更加有效的手写SQL覆盖你的ORM方法。不是吗?

ORM的问题

1. 不充分的抽象学习

ORM最明显的问题是它并不能彻底从实现细节中抽象出来。全部主流ORM的文档中处处都引用了SQL的概念。其中一些介绍的时候并不会代表其在SQL中的等价物,而其余一些则将库看做用来生成SQL的过程函数。优化

抽象的要点在于它应该使问题得以简化。对SQL进行抽象,同时又要求你懂得SQL,这使得你须要学习的东西成倍增长了:首先,你必须理解你正在试图执行的SQL是什么,而后你还要学习ORM的API,来让它为你编写这些SQL。在Hibernate中,为了完成复杂的SQL你甚至须要学第三种语言:HQL,它几乎就是SQL(但又不彻底是),其在幕后被翻译成SQL。.net

ORM的支持者会辩解说并不是每一个项目都是如此,并不是每一个人都须要复杂的join,而且ORM是一个"80/20"解决方案,其中80%的用户只须要SQL中20%的功能,ORM能够处理这些问题。我能说的是,我15年来编写web应用的数据库后端的经历代表,事实并不是如此。只有在项目刚开始的时候你不须要join和本地join。在那以后,你就要优化和巩固你的查询。即便80%的用户只用到SQL中30%的功能,但是100%的用户都须要打破ORM的抽象才可以完成工做。翻译

2. 不正确的抽象

若是你的项目确实不须要任何关系数据功能,那么ORM能够很是完美地为你工做。可是接下来你又遇到另一个问题:你用错了了数据存储。关系存储的额外付出是很是高的;这就是为何NoSQL数据要快得多的重要缘由之一。然而,若是你的数据是关系型的,那么额外的付出就是值得的:你的数据库不只存储数据,它还表达了你的数据,而且能够基于关系概念回答关于它的问题,这比你用过程代码可以作到的要快速得多。

可是,若是你的数据不是关系型的,那么你就是在不适当的场合使用SQL,这为你增长了巨大且没必要要的负担;为了让问题更加严重,你在其上又增长了一重额外的抽象。

另外一方面,若是你的数据是关系型的,那么你的对象映射最终会失败。SQL是关于关系代数的:SQL的输出不是对象,而是对于某个问题的解答。若是你的对象“是一个”X的实例,而且“拥有一些”Y,且每一个Y“属于”Z,那么对象在内存中正确的表达形式是什么? 它应该是X的属性,或者所有包含在Y中,或者/而且所有包含在Z中?若是你只获得X的属性,那么什么时候你运行查询来得到Y呢?并且,你是想要其中一个仍是所有?现实中,答案是依赖于条件的:这就是为何我说SQL是对于问题的回答。对象在内存中的表达形式取决于你的意图,然而面向对象设计没有依赖于上下文的表达这样的功能。关系不是对象;对象也不是关系。

3. 多个查询致使失败

这天然的引出了ORM的另外一个问题:效率低下。当你获取一个时,你须要哪些属性?ORM并不知道,因此它老是取得所有(或者它要求你告诉它,可是这又打破了抽象)。开始的时候这不成问题,可是当你一次取出上千条纪录的时候,若是你只须要3个属性却不得不取出所有30列,这时就产生了严重的性能问题。许多ORM层很是不善于推断join,从而不得不使用分离的查询来获取关联数据。如前所述,许多ORM层明确声明效率将会有所牺牲,其中一些提供了某些机制来调整引发问题的查询。我从过去的经历中发现的问题代表,不多有只须要调整单个“银弹”查询的状况:应用的数据库后端之因此死掉不是由于其中某一条查询,而是众多的查询引发的。ORM缺乏上下文敏感的性质意味着它没法巩固查询,相反必须借助cache或其余机制来进行必定程度的补偿。

那么替代方案是什么?

但愿到这里我已经澄清ORM在设计上的一些缺陷。可是要做为一个反模式,还须要存在替代的解决办法。事实上有两个取代方法:

1. 使用对象

若是你的数据是对象,那么中止使用关系数据库。编程界当前正在出现键-值对存储的浪潮,它容许你以闪电般的速度访问优雅的、自我包含的海量数据。没有法律规定编写Web应用的第一步必须安装MySQL。对于对象的每一种表达方式都使用关系数据库是一种过分使用,这也是近几年SQL的名称不太好的缘由之一。事实上,问题在于偷懒的设计。

2. 在模型中使用SQL

编程中做任何事情都只有一种正确的方式,这是一种危险的说法。然而根据个人实践,在面向对象的代码中表达关系模型的最佳方法仍然是模型层:将你的全部数据表示封装在一个单独的区域是一个好注意。然而,记住模型层的工做簿在于表达对象,而在于回答问题。提供一个能够回答你的应用程序所包含的问题的API,尽可能保持简洁高效。有时候,这些回答显得格格不入,以至于看上去是“错误的”,甚至对于资深的OO开发者也是如此。可是,你能够根据经验来更好地找到其中的广泛性,从而容许你将多个查询方法重构为单个。

相似的,有时候输出会是单个对象X,它很容易表达。 可是也有时候输出是聚合的对象表格,或者单个整数值。你要忍住将这些内容用过多抽象来包装的诱惑,用对象自身的术语来描述。首要的是,不要相信OO可以表达任何对象和全部对象。OO自己是一种优美和灵活的抽象,但关系数据在其范围以外,把它不能表达的东西假装成对象是ORM的核心与真正的问题。

总结

  • ORM最初比编写基于SQL的模型代码更快,也更容易理解
  • 它在任何项目早期都是足够有效的
  • 不幸的是,这些优势在项目复杂性提高的时候就消失了:抽象被打破,开发者被迫使用并理解SQL
  • 彻底是非正式的声明,我认为ORM对抽象的破坏不是仅仅涉及20%的项目,而是几乎100%。
  • 对象并不足以充分表达关系查询的结果。
  • 关系查询映射到对象的不充分性致使了ORM后端应用的效率低下,这些问题广泛分布在应用的各处,而且除了彻底放弃ORM以外,没有简单的解决办法。
  • 不要对任何问题都使用关系存储与ORM,而是更加仔细地思考你的设计
  • 若是你的数据天生就是对象,那么请使用对象存储("NoSQL")。它们要比关系数据库快得多。
  • 若是你的数据天生就是关系型的,那么关系数据库带来的开销是值得的。
  • 把你的关系查询封装在模型层中,设计你的API从而为应用提供数据访问支持;拒绝过度泛化的诱惑。
  • 面向对象没法以有效的形式表达关系数据;这是面向对象设计的一个基本限制,ORM没法修复它。
相关文章
相关标签/搜索