ORM有哪些不使用的好理由?

119

在我的学徒期间,我曾经在一些较小的项目中使用NHibernate,这些项目大多由我自己编码和设计。现在,在开始一些更大的项目之前,讨论如何设计数据访问以及是否使用ORM层。由于我仍处于学徒阶段,并且仍然认为自己是企业编程的初学者,因此我没有真正试图推动我的观点,即使用对象关系映射器可以极大地简化开发。开发团队中的其他程序员比我经验丰富得多,所以我想我只能听从他们的意见。:-)

然而,我并不完全理解不使用NHibernate或类似项目的两个主要原因:

  1. 可以使用SQL查询构建自己的数据访问对象,并将这些查询从Microsoft SQL Server Management Studio复制出来。
  2. 调试ORM可能很困难。

当然,我可以使用大量的SELECT等构建自己的数据访问层,但是在这里,我错过了自动连接、延迟加载代理类以及如果表格添加新列或列重命名时的更低维护工作量的优势。(更新众多的SELECTINSERTUPDATE查询与更新映射配置以及可能重构业务类和DTO之间的区别。)

此外,使用NHibernate,如果您不太了解该框架,可能会遇到意想不到的问题。例如,在Table.hbm.xml中设置字符串长度以进行自动验证。然而,我也可以想象在基于“简单”SqlConnection查询的数据访问层中出现类似的错误。

最后,上述论点是否真的是不利用ORM进行非平凡数据库企业应用程序的好理由吗?他们/我可能错过了其他论点吗?

(我应该补充说明,我认为这是第一个需要团队合作的“大型”基于.NET/C#的应用程序。目前在这里还不存在如Stack Overflow上被视为相当正常的良好实践,例如单元测试或持续集成。)

我认为这可能是第一个需要团队协作的“大型”基于.NET/C#开发的应用程序。迄今为止,在这个项目中缺少如Stack Overflow上被认为是很正常的良好实践,例如单元测试或持续集成。请注意,原文中保留了HTML标签。
20个回答

69

简短回答是可以,而且有很好的原因。实际上,在某些情况下,你只能使用存储过程。

以我工作的大型企业金融机构为例,我们必须遵守许多安全准则。为了满足强加给我们的规则和法规,通过审核的唯一方法就是将数据访问限制在存储过程中。现在有些人可能会认为这很愚蠢,但老实说这并不是这样。使用ORM工具意味着工具/开发人员可以插入、选择、更新或删除任何想要的内容。特别是在处理客户数据的环境中,存储过程提供了更多的安全性。我认为这是考虑的最重要原因。安全性。


26
我认为这更多是当前 ORM 库无法支持存储过程的限制,而不是OR映射中的基本限制。有些 ORM 能够处理存储过程和视图,ORM+存储过程解决方案有很多值得一提的地方。 - Mendelt
4
如果使用良好的 ORM 工具,你应该能够让它使用存储过程(当然,这也会减轻很多优点……) - kͩeͣmͮpͥ ͩ
3
为什么存储过程并没有比ORM提供更多的安全性,这个话题已经有了很多论述。这里有一篇文章很好地解释了这个问题:http://ayende.com/Blog/archive/2007/11/18/A-false-sense-of-security.aspx - Tim Scott
2
当然可以限制用户使用 ORM 的功能。您只需在 SQL 中设置用户安全设置,并赋予他们插入/更新/删除所需的特权。这与为存储过程设置安全权限相同。 - Doctor Jones
1
这似乎是一种政策(因此是外在的)限制,而不是工具本身的限制。因此,这可能不是最合适的答案。 - Najeeb
显示剩余6条评论

67

ORM的甜蜜点

ORM对于自动化适用的95%以上的查询非常有用。它们的特定优势就是在您拥有具有强大对象模型架构和与该对象模型良好配合的数据库的应用程序时。如果您正在进行新的构建并且团队中具有很强的建模技能,那么使用ORM可能会得到良好的结果。

您可能会遇到一些最好手动处理的查询。在这种情况下,请不要害怕编写一些存储过程来处理此问题。即使您打算在多个DBMS平台上移植您的应用程序,依赖于数据库的代码也将占少数。请记住,您需要在您打算支持它的任何平台上测试您的应用程序,因此为某些存储过程付出一点额外的移植工作量不会对您的TCO产生太大影响。初步估计,98%的可移植性和100%的可移植性一样好,远远优于为了解决ORM限制而使用复杂或性能差的解决方案。

我曾经在一个非常大(数百人年)的J2EE项目中看到过这种方法的成功运用。

ORM可能不适合的情况

在其他情况下,可能有比ORM更适合应用程序的方法。Fowler的《企业应用程序架构模式》中有一个关于数据访问模式的部分,对此进行了相当好的分类。我看到一些情况下ORM可能不适用的例子是:

  • 在具有大量已存在存储过程的遗留代码库的应用程序上,您可能希望使用面向功能(不要与函数式语言混淆)的数据访问层来包装现有的sproc。这将重复使用现有的(因此经过测试和调试的)数据访问层和数据库设计,这通常代表了相当大的开发和测试工作,并且节省了将数据迁移到新数据库模型的时间。这通常是在遗留的PL/SQL代码库周围包装Java层或使用Web接口重新定位丰富客户端VB、Powerbuilder或Delphi应用程序的不错方式。

  • 有时候你会继承一份数据模型,它并不适合使用对象-关系映射。例如,如果你在编写一个与外部接口交互的接口,你可能最好直接与数据库打交道。

  • 金融应用程序或其他需要跨系统数据完整性的系统,特别是当你使用带有两段提交的复杂分布式事务时。你可能需要比 ORM 更好地微观管理你的事务。

  • 在高性能应用程序中,你可能想要真正优化你的数据库访问。在这种情况下,更低层次的工作可能更可取。

  • 如果你正在使用类似于 ADO.Net 的现有数据访问机制,而且这个机制已经足够好了,并且与平台良好地协作,那么这样做比使用 ORM 更有益。

  • 有时候数据只是数据 - 例如,你的应用程序处理的是“交易”而不是“对象”,这是一个明智的领域视角。一个例子是金融包,其中你可以配置交易分析字段。虽然应用程序本身可能是基于面向对象的平台构建的,但它并没有与单个业务域模型绑定,并且可能仅意识到 GL 代码、账户、文档类型和半打分析字段。在这种情况下,应用程序并不意识到业务域模型,对象模型(除了账户结构本身)对应用程序是不相关的。


除了社交网站之外,如果数据完整性不重要,为什么还要构建系统呢?这也适用于其他类型的需要保证数据完整性的系统。 - ObiWanKenobi
3
该论点特别涉及分布式事务,其中多个系统必须同步或通过消息队列进行提交或回滚的保证。并非所有这些系统都会为您构建,并且因此不一定支持ORM。您可能需要显式地管理事务,这可能需要您使用比ORM更低级别的工具包。 - ConcernedOfTunbridgeWells
2
我知道已经过去一年了,但是因为你提到有时数据只是数据这个事实,所以给你点赞。 - luis.espinal

39
首先,使用ORM并不会使您的代码更容易测试,也不一定会在持续集成场景中提供任何优势。
根据我的经验,虽然使用ORM可以增加开发速度,但您需要解决的最大问题是:
1.测试您的代码 2.维护您的代码
这些问题的解决方案是:
1.使您的代码可测试(使用SOLID原则) 2.为尽可能多的代码编写自动化测试 3.尽可能经常运行自动化测试
回到您的问题,您列出的两个反对意见似乎更像是无知,而不是其他什么原因。
无法手动编写SELECT查询(我想这就是为什么需要复制粘贴的原因)似乎表明迫切需要进行一些SQL培训。
我不使用ORM的两个原因是:
1.公司政策严禁使用ORM(在这种情况下,我会去其他地方工作) 2.该项目非常数据密集,使用供应商特定的解决方案(如BulkInsert)更有意义。
通常关于ORM(特别是NHibernate)的反驳如下:
  1. 速度

    使用ORM不会比手写数据访问慢,事实上,由于内置的缓存和优化,它可能更快。 好的ORM将生成一组可重复查询,您可以通过优化模式来优化您的架构。 一个好的ORM还将允许使用各种提取策略高效检索关联数据。

  2. 复杂性

    关于复杂性,使用ORM意味着更少的代码,通常意味着更少的复杂性。 许多使用手写(或代码生成)数据访问的人发现自己在“低级”数据访问库上编写自己的框架(例如为ADO.Net编写帮助方法)。这等同于更多的复杂性,更糟糕的是,它们很少有良好的文档或经过充分测试。
    如果您特别关注NHibernate,则像Fluent NHibernate和Linq To NHibernate这样的工具也可以降低学习曲线。

关于整个ORM争论的问题是,声称使用ORM会太难/慢/无法处理的人是那些非常乐意使用Linq To Sql或Typed Datasets的人。虽然Linq To Sql是朝着正确方向迈出的一大步,但它仍然落后于一些开源ORM。但是,针对Typed Datasets和Linq To Sql的框架仍然非常复杂,并且将它们用于超出(Table=Class)+(基本CRUD)的范围是非常困难的。

我的建议是,如果最终无法获得ORM,则确保您的数据访问与其余代码分离,并遵循四人帮的建议编写接口。此外,获取依赖注入框架以进行连接。
(对此有何反驳?)

35

ORM 工具(如 Hibernate)可以解决许多常见问题,但有些情况下它可能会成为麻烦。我不了解您的项目足够详细,无法确定它是哪种情况。

Hibernate 的一个优点是您只需编写三次代码:属性在类中、.hbm.xml 文件中和数据库中均有声明。而对于 SQL 查询而言,每个属性都需要在类中、数据库中、select 语句、insert 语句、update 语句、delete 语句以及支持 SQL 查询的所有编组和解组代码中进行声明。这很快会变得混乱起来。但是,您知道它是如何工作的,可以轻松调试。它全部都在您自己的持久层中,而不是隐藏在第三方工具的内部。

Hibernate 可能是 Spolsky's 泄漏抽象定律的代表。如果稍微偏离正轨,您需要深入了解工具的内部运作。当您知道自己只需花费几分钟即可修复 SQL,但是却花费数小时将 dang 工具强制转换为合理的 SQL 时,这可能会非常烦人。有时候调试是一场噩梦,但很难说服那些没有亲身体验过的人。

编辑:如果他们不打算使用 NHibernate,并且希望控制自己的 SQL 查询,您可能需要了解一下 iBatis.NET。

编辑2:但是,这里有一个很大的警告:“尽管在 Stack Overflow 上,像单元测试或持续集成这样的良好实践被视为非常正常的事情,在这里却不存在。”那么,这些“有经验”的开发人员,他们有什么开发经验呢?他们的工作安全性吗?看起来您可能是与不太感兴趣的人打交道,所以不要让他们扼杀您的兴趣。您需要保持平衡,进行反击。


3
重复不再正确。如果你使用JPA注解,你只需要指定一次即可。Hibernate甚至会为您构建数据库创建语句,虽然这对于确定您的映射是否正确最有用。 - jonathan-stafford
对问题进行了很好的平衡讨论。我的实体框架经验恰好符合您所描述的“花费数小时试图哄骗您的工具”和“很难说服那些没有经历过的人”的情况。它显然更快地编写,但是使其真正高效执行是一个巨大的时间浪费者。 - user334911
4
已经过去了8年,但这句话仍然是正确的:“当你知道你可以在几分钟内修复SQL,但你却花费数小时试图说服那该死的工具生成合理的SQL时,会非常恼人。” - Ruslan Stelmachenko

29
近年来,ORM的增长爆炸式增长,而你更有经验的同事可能仍然沿用“每个数据库调用都应通过存储过程”这种思维方式。
为什么ORM会使调试变得更加困难?无论结果是来自存储过程还是ORM,你将获得相同的结果。
我想,我能想到的唯一真正的劣势就是ORM的安全模型略逊于灵活性。
编辑:我刚刚重新阅读了你的问题,看起来他们正在将查询复制并粘贴到内联SQL中。这使得安全模型与ORM相同,因此这种方法没有任何优势。如果他们使用未参数化的查询,则实际上存在安全风险。

1
更难调试:如果抛出异常,您可能需要知道框架而不是了解SqlConnection的异常等。 - hangy
4
SQL异常不是ORM异常的一部分吗? - Giovanni Galbo
1
是的,大多数情况下都会包装异常,因此您仍然可以通过 .inner 获取原始异常。 - mattlant
还有一件事...当你在代码中使用事务时,它们会花费更多的时间。这意味着你更容易遇到数据库同步问题,因此如果你有一个非常高并发的系统,至少一些逻辑必须放在存储过程中。 - Uri Abramson
@uri True; 我绝对不反对在需要高性能或专业数据库功能且ORM无法提供的领域中使用存储过程。 - Giovanni Galbo
显示剩余2条评论

13

我曾经参与过一个项目,其中成功地没有使用ORM。这是一个需要从一开始就进行水平扩展的项目,需要快速开发,并且具有相对简单的领域模型。

  1. 必须从一开始就可以进行水平扩展
  2. 需要快速开发
  3. 具有相对简单的领域模型

如果要让NHibernate在水平分区结构中工作,所需的时间比开发一个非常简单的数据映射器,该数据映射器了解我们的分区方案的时间要长得多...

因此,在我参与的90%的项目中,ORM都是非常有价值的帮助。但是,在某些非常特定的情况下,我认为不使用ORM是最好的选择。


你提出了一些反对在某些特定情况下使用ORM的好观点。然而,这个项目属于那90%的项目之一,ORM可能有助于降低开发和维护成本。 - hangy
完全同意 - 抱歉没有直接回答你的问题!我相信你提到的具体原因是相当无效的 :) - Mike
水平扩展NHibernate:http://www.blechie.com/WPierce/archive/2008/06/08/Poor-Mans-Shards-in-NHibernate.aspx,http://darioquintana.com.ar/blogging/?p=25 - Mauricio Scheffer

11

首先,我要说的是如果正确集成,ORM可以让您的开发生活更轻松。但有一些问题,ORM实际上可能会阻止您实现所述的要求和目标。

我发现在设计具有高性能要求的系统时,我经常挑战自己找到使系统更具性能的方法。很多时候,我最终得到的解决方案具有很高的写入性能配置文件(这意味着我们写入数据的次数比读取数据的次数要多)。在这些情况下,我希望利用数据库平台为我提供的设施以达到我们的性能目标(它是OLTP而不是OLAP)。因此,如果我使用SQL Server并且知道我有大量数据要写入,那么为什么不使用批量插入……嗯,正如您可能已经发现的那样,大多数ORM(我不知道是否有一个)都无法利用特定于平台的优势,例如批量插入。

您应该知道,您可以混合使用ORM和非ORM技术。我只是发现有一些边缘情况,ORM无法支持您的要求,您必须在这些情况下进行解决。


11

对于一个规模不小的基于数据库的企业应用程序,没有使用ORM是没有合理解释的。

尽管功能可能略有不同:

  • 如果不使用ORM,则正在解决已被大型社区或资源丰富的公司反复解决的问题。
  • 通过使用ORM,您的数据访问层的核心部分将受益于该社区或公司的调试工作。

为了客观地评估这个争论,考虑使用ADO.NET与编写解析表格数据流的代码之间的优势。

我看到过对如何使用ORM的无知来证明开发人员对ORM的厌恶。例如:急切加载(我注意到您没有提到这一点)。想象一下,您想检索客户及其所有订单,以及其中所有订单详细信息。如果仅依靠延迟加载,您将从ORM体验中得出结论:“ORM很慢。”如果您学会了如何使用急切加载,您将用5行代码在2分钟内完成同样的操作,而您的同事则需要花费半天的时间:向数据库发出一个查询并将结果绑定到对象的层次结构中。另一个例子是手动编写SQL查询以实现分页的痛苦。

不使用ORM的可能例外情况是,如果该应用程序是设计为应用专业业务逻辑抽象的ORM框架,并设计用于在多个项目中重复使用。即使是这种情况,通过增强现有ORM的这些抽象也可以更快地实现采用。

不要让您的高级团队成员的经验将您拖入计算机科学演进的相反方向。我已经职业开发了23年,其中一个恒定因素是老派排斥新事物。ORM对SQL是C语言对汇编语言的影响,您可以肯定地认为,等同于C++和C#的工具正在研发之中。一行新式代码相当于20行老式代码。


11

我认为不使用ORM有很多好的理由。首先,我是一名.NET开发人员,我喜欢坚持使用.NET框架已经为我提供的东西。它已经能够满足我所有的需求了。通过这样做,您将保持更标准化的方法,因此在未来任何其他开发人员需要继续开发该项目时,有更好的机会能够理解和运用。微软已经提供的数据访问功能相当充足,没有理由去抛弃它们。

我已经是一名专业开发人员10年了,领导过多个百万美元级别的成功项目,而且我从未编写过需要能够切换到任何数据库的应用程序。你为什么要让客户这样做呢?仔细计划,选择适合你所需的正确的数据库,并坚持使用它。就我个人而言,SQL Server已经能够满足我所需的所有功能。它易用且非常出色。甚至还有一个支持高达10GB数据的免费版。噢,它还与.NET完美配合使用。

最近我不得不开始处理几个使用ORM作为数据层的项目。我认为这很糟糕,是我不得不学习如何使用它们,而这完全没有必要。在极为罕见的情况下,客户确实需要更改数据库,我可以很容易地重新设计整个数据层,而这比我花费在ORM提供商上的时间还要少。

老实说,我认为ORM只有一个真正的用途:如果您正在构建像SAP这样的应用程序,确实需要在多个数据库上运行的能力。否则,作为一个解决方案提供商,我告诉我的客户,该应用程序是设计为在此数据库上运行的,这就是它的工作方式。再次强调,在10年和无数个应用程序之后,这从来不是问题。

否则,我认为ORM适用于不理解“少即是多”的开发人员,并认为在其应用程序中使用更多酷炫的第三方工具可以使其应用程序更好。 同时,我会继续编写更多出色的软件,而这些软件任何开发人员都可以立即获得并提高生产力,将此留给铁杆技术极客们处理。

5
我真的不明白为什么这个答案被投票否决。利用环境提供的工具来保持简单,是很好的实践。作为一名经验丰富的清洁代码专家,我发现ORM很难阅读,因此也很难维护。当应用程序中存在复杂关系时(最终您一定会遇到这种情况),您无法准确知道执行哪些查询。长期来看,调试这样的混乱将变得越来越困难。我的建议是,保持简单,不要使用ORM。 - David
这很有趣。我已经做了24年的开发人员,从未参与过只支持一个RDBMS供应商的项目。每个项目都要求我们支持多个RDBMS供应商,因为我们需要足够灵活,能够与需要Oracle的客户和需要SQL Server的其他客户一起工作。如果您在真空中构建独立应用程序,那么您可以仅规定RDBMS。但是我从未参与过接受这种做法的项目。 - deltamind106
我有很多应用程序可以支配关系数据库管理系统,主要是因为在许多内部应用程序和面向客户的应用程序中查看“我们”的数据。我也是一名开发人员,已经有24年的经验了。 - jeffkenn

9
当你需要更新50000000条记录时,请设置一个标志或其他内容。
尝试使用ORM进行此操作,而不调用存储过程或本地SQL命令。
更新1:还可以尝试仅检索具有少量字段的一条记录(当您拥有非常“宽”的表格时),或标量结果。ORM在这方面也很差。
更新2:据称EF 5.0 beta承诺进行批量更新,但这是非常热门的消息(2012年1月)。

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-direct - dotjoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接