使用ORM还是纯SQL?

305

对于我开发的一些应用程序(然后就忘记了),我一直在编写纯SQL,主要针对MySQL。虽然我已经使用过像SQLAlchemy这样的Python ORM,但我并没有长期坚持使用它们。通常是文档或者复杂性(从我的角度来看)让我望而却步。

我认为:如果只使用一种类型的数据库,可以使用纯SQL,如果需要可移植性,则使用ORM。我真的想寻求关于在开发需要数据库支持的应用程序时何时使用ORM或SQL的建议。

思考一下,与使用ORM相比,仅使用轻量级包装器来处理数据库不一致会更好。


2
标准化、安全性、可维护性、语言抽象、DRY等。 - Ben
ORM的性能可以接近SQL,这取决于您是否正确使用它以及使用正确的设置...请参阅如何使EF6.x快5倍: https://www.linkedin.com/pulse/orm-read-performance-ef-vs-dapper-balazs-hideghety - baHI
关于ORM架构和如何操作(需要避免的问题),这是我的另一个链接:https://www.linkedin.com/pulse/get-know-your-orm-avoid-bad-habits-balazs-hideghety - baHI
对象关系映射(ORM)已经在许多编程语言中非常流行,是SQL的最佳替代方案之一。我受到方法链接风格的启发,为我的TRIADB项目创建了CQL。healis.eu/triadb/#latest-release - Athanassios
4
ORM是不懂SQL或懒得编写SQL的借口。如果您不懂SQL,请使用ORM。但不要浪费时间认为使用ORM会减少工作量,这是一种错误的信仰。可移植性方面,如果您的项目需要跨多个数据库进行移植,则可以选择ORM。速度方面,全力使用SQL,因为ORM不够快。可靠性方面,我会坚持使用SQL。安全性方面,两者都没有太多问题需要担心。总之,这取决于您的项目需求,特别是关于数据库可移植性方面。 - pasignature
12个回答

306

作为一个曾经花费很多时间工作于JPA(Java Persistence API,基本上是Java/J2EE/EJB的标准ORM API),包括Hibernate、EclipseLink、Toplink、OpenJPA等,我想分享一些自己的观察。

  1. ORM不够快速。它们可能是足够的,大部分时间足够就可以了,但在高并发低延迟的环境下,它们是不可取的;
  2. 在像Java和C#这样的通用编程语言中,你需要很多魔法才能让它们工作(例如Java中的加载时织入,仪器化等);
  3. 当使用ORM时,与其远离SQL(似乎是意图),你会惊讶地发现,你花费了多少时间来调整XML和/或注释/属性,以使你的ORM生成性能良好的SQL;
  4. 对于复杂的查询,真的没有替代品。就像在JPA中,有一些查询是原始SQL中根本不可能的,在JPA中使用原始SQL并不美观(C#/.Net至少有动态类型--var--比对象数组要好得多);
  5. 使用ORM时,有很多"坑”。这包括无意间或意外的行为,事实上,你必须建立能够对数据库进行SQL更新的能力(通过在JPA中使用refresh()或类似方法,因为JPA默认缓存所有东西,所以它不会捕捉到直接的数据库更新--运行直接的SQL更新是常见的生产支持活动);
  6. 对象关系不匹配问题总是会引起问题。对于任何这样的问题,抽象的完整性和复杂性之间存在权衡。有时我觉得JPA走得太远了,遇到了真正的收益递减规律,复杂度过高并不能得到合理的抽象。

还有一个需要更详细解释的问题。

Web应用程序的传统模型是具有持久层和表示层(可能有服务或其他层位于其中,但这些对本讨论非常重要)。ORM强制从持久层向上到表示层(即你的实体)看待问题。

更原始的 SQL 方法之一的批评是,你最终会有很多仅用于一个查询的 VOs(值对象)或 DTOs(数据传输对象)。ORM 的优点在于消除了这些问题。

但实际上,这些问题并没有随着 ORM 而消失,而是转移到了表示层。你需要为每个视图创建自定义表示对象,通常为每个视图创建一个。这比创建查询的 VOs/DTOs 好在哪里呢?在我看来并没有什么好处。

我在ORM 或 SQL:我们到达了哪里?中对此进行了讨论。

我现在(在 Java 中)选择的持久化技术是 ibatis。它是一个相当轻量级的 SQL 包装器,可以完成 JPA 的 90%+ 功能(甚至可以进行关系的懒加载,尽管文档记录不太清楚),但开销要少得多(包括复杂性和实际代码方面)。

去年我写 GWT 应用程序时遇到了这个问题。在服务实现中从 EclipseLink 转换为表示对象非常麻烦。如果我们使用 ibatis,创建适当的对象然后将它们传递上下整个堆栈会更加简单。有些纯粹主义者可能会认为这是不好的™。也许是(从理论上来说),但我告诉你:这将导致更简单的代码、更简单的堆栈和更高的生产力。


3
iBATIS很好,但也许你想试试jOOQ:http://jooq.sourceforge.net。它的主要重点正是为了实现你提到的六个接近SQL的原因。 - Lukas Eder
6
点3加1。许多人认为使用ORM可以减轻你对SQL的深入理解的需求。问题是,一旦你能够/学会使用SQL进行体操,你很可能会非常快地摆脱ORM。 - Ryan Fernandes
4
现在是2013年底,众所周知,“旧事实”可能比什么都更具误导性。我想问一下您的观点是否依然不变?如果有所改变,能否写一篇博客文章或更新您的回答,这将非常好。 - Dominik
3
在.NET中,var不会产生动态类型,使用dynamic关键字的变量才是动态类型。var仍然属于静态类型。请参考 https://dev59.com/_nNA5IYBdhLWcg3wcNXF - Fazi
2
我完全同意。使用NHibernate已经5年了,我真的不想再接触ORM了。我很熟悉SQL,可以处理一些简单的查询和插入操作。 - Alejandro
显示剩余10条评论

205
ORM拥有一些不错的特点。它们可以处理数据库列与对象字段之间的大量重复工作。它们通常将语言日期和时间类型转换为适当的数据库类型。它们通过实例化嵌套对象来优雅地处理一对多关系。我发现,如果你在设计数据库时考虑ORM的优缺点,可以节省很多从数据库中获取数据和插入数据的工作量。(如果需要映射多态和多对多关系,您需要知道它如何处理它们。正是这两个领域提供了大部分“阻抗不匹配”,使一些人称ORM为“计算机科学的越南”)

对于事务性应用程序,即您发送请求、获取一些对象、遍历这些对象以获取一些数据并在Web页面上呈现它们的情况下,性能损失很小,而且在许多情况下,ORM可能会更快,因为它会缓存它之前见过的对象,否则将多次查询数据库。

对于报表密集型或每个请求处理大量数据库行的应用程序,ORM的负担要重得多,并且它们做的缓存会变成一个庞大而无用的内存负担。在这种情况下,简单的SQL映射(LinQ或iBatis)或在薄的数据访问层中手动编写SQL查询是正确的选择。

我发现,对于任何大型应用程序,您会发现自己同时使用这两种方法(ORM用于简单的CRUD和SQL/薄数据访问层用于报表)。


1
你能定义一下“每个请求的大量数据库行”吗?谢谢 :) - Mosselman
我可以将JPA与IBatis集成吗?并且让它们在同一个事务中工作? - Jaime Hablutzel
3
没有人讨论的另一个问题是基本状态管理。这整个框架堆栈(JSF、JPA等)都基于Java bean的get/set方法。对于每个表和每个列,这是大量样板文件... 这里是真正的反模式:只公开每个字段。实际上,在对象/表/行中的字段上有get/set方法非常接近于违反信息隐藏和封装的每个租户。最后,回到状态管理...不可变选项在哪里?半置对象是否允许?大多数情况下没有选择。 - Darrell Teague
2
我想要聚焦并特别强调这个答案中的一个关键陈述:“对于每个请求处理大量数据库行的应用程序,ORM 的负担更重。” ORM 只适用于开发人员和维护,因为大多数开发人员不擅长 SQL,但如果你真正关心性能,SQL 完全胜过它。 - Manachi
大多数开发人员并不擅长SQL。我认为,大多数开发人员不知道如何正确使用LINQ、表达式树和ORM等工具,也不了解代码生成和其他许多方面的知识。但是,我没有任何依据来做出这样强烈的说法。 - Adanay Martín

71

我认为对于读取操作应该使用普通的SQL语句,而对于增、删、改操作可以使用ORM。

性能一直是我非常关注的问题,特别是在Web应用程序中,但也包括代码可维护性和可读性。为了解决这些问题,我编写了SqlBuilder


3
CUD是什么?我找不到定义。 - Kimchi Man
53
@KimchiMan CRUD没有R。 - Max Toro
10
CUD - 创建、更新、删除。 - Combine

16

ORM不仅是可移植性(即使使用ORM,这也很难实现)。它为您提供的基本上是一个持久性存储的抽象层,在ORM工具中,它使您免于编写样板SQL查询(按主键或谓词选择、插入、更新和删除),并让您集中精力解决问题领域。


3
我在考虑更接近各种数据库之间可移植性的事情。我不应该在深夜发布问题。 - hydrapheetz
1
这正是我所说的:即使是最基本的情况也可能在不同的DBMS中出现错误,例如对NULL的处理方式不同。 - Anton Gogolev
1
ORM(对象关系映射)为对象之间的关系提供了一层抽象,但就你提到的样板查询而言,并没有太大的优势。在JDBC应用程序中,您可以在抽象超类或实用程序类中编写这些类型的查询,只需少量代码即可。无需为每个新表重复样板。 - KevinS

12

任何值得尊重的设计都需要对数据库进行一定程度的抽象,以处理阻抗失配。但最简单的第一步(对于大多数情况来说已经足够了)我期望是使用数据访问层(DAL),而不是笨重的ORM。你的选择不应该只在两个极端之间。


回答一条评论要求我描述如何区分DAL和ORM:

DAL是你自己编写的代码,可能从一个仅封装表格并将其字段映射为属性的类开始。ORM是代码,您不需要为其编写抽象机制,而是通过推断您的DBMS模式的其他属性(主键和外键)进行的。(这就是您发现自动抽象开始变得不完整的地方。我更喜欢有意识地告知它们,但这可能只是我的个人偏好)。


2
你如何区分什么是数据访问层(DAL),什么是对象关系映射(ORM)? - chaos
8
所以,如果你是一个ORM的作者,你的ORM自动转化为DAL了? :) - Bombe
DAL是持久层,ORM是您在DAL内部使用的工具,用于执行CRUD操作并将数据存储到数据存储区。 - Vahid Ghadiri

10

使我的ORM使用真正飞起来的关键是代码生成。我同意ORM路线在代码性能方面并不是最快的。但是当你有一个中等到大型的团队时,数据库正在快速变化,从数据库重新生成类和映射作为构建过程的一部分是令人惊叹的东西,特别是当你使用CI时。因此,你的代码可能不是最快的,但你的编码将是最快的——对于大多数项目,我知道我会选择哪个。

我的建议是,在架构仍然不确定的情况下使用ORM进行开发,使用性能分析来找到瓶颈,然后使用原始Sql来调整需要优化的区域。

另外一个想法是,如果以正确的方式使用Hibernate内置的缓存功能,可以经常实现巨大的性能提升。不再返回DB以读取参考数据。


2
完全是个人喜好问题。对我来说,代码生成是一个缺陷。 - dkretz
5
阅读第二段...或许完整性也是有用的。 - MrTelly
代码生成是完成某些任务更快的唯一方法。就像所有工具一样,它可以强大或导致灾难。从技术上讲,所有语言都会产生其他类型的代码。 - Banjocat

9
在现代软件开发中,使用框架或不使用框架的困境是非常普遍的。需要理解的重要一点是,每个框架或方法都有其优缺点 - 例如,在我们的经验中,我们发现ORM在处理事务即插入/更新/删除操作时很有用,但是当涉及到获取具有复杂结果的数据时,评估ORM工具的性能和有效性变得重要。
此外,重要的是要理解,并不强制选择一个框架或方法并在其中实现所有内容。我们的意思是,我们可以混合使用ORM和本地查询语言。许多ORM框架提供扩展点以插入本机SQL。我们应该尽量避免过度使用框架或方法。我们可以结合某些框架或方法,得出适当的解决方案。
当涉及到高并发的插入、更新、删除、版本控制时,可以使用ORM,而对于报告生成和长列表,可以使用本地SQL。

3
为什么ORM适用于高并发环境? - user359996

4

没有一种通用的解决方案,这也适用于“我是否应该使用ORM”的问题。

我的建议是:如果您需要编写一个非常“数据”集中、没有太多其他逻辑的应用程序/工具,则我会使用纯SQL,因为SQL是这种应用程序的特定领域语言。

另一方面,如果我要编写一个包含大量“领域”逻辑的业务/企业应用程序,则我会编写一个丰富的类模型,可以在代码中表达这个领域。在这种情况下,ORM映射器可能非常有用,因为它会将很多冗长的代码交给您处理。


“没有‘一刀切’的解决方案”...嗯,其实应该有的。 - Rushino

1
我开发过的应用之一是用Python编写的IRC机器人。它使用的模块在单独的线程中运行,但我还没有找到在使用sqlite时处理线程的方法。不过,这可能更适合作为一个单独的问题。
我真的应该重新措辞标题和实际问题。我从未在任何语言中使用过DAL。

4
我认为你应该这样做。到处都是原始的SQL代码非常糟糕。 - chaos
嗯,是的。有一款论坛软件我时不时会进行一些修改,里面到处都是大量的mysql_query()和mysql_result()。这太疯狂了。 - hydrapheetz
你所说的“app”是什么? - Zoran Pavlovic
有趣的是,这个问题是在irc机器人应用程序上提出的,并成为了一个非常有用的指南! irc机器人应用程序位于规模的一端,而具有50-100个以上表格、复杂连接和数百万行数据以及20多个开发人员参与的应用程序则位于另一极端。我敢说,当涉及到“irc机器人应用程序”这一端时,这几乎无关紧要。 - Manachi

1
我知道这个问题很老,但我认为我应该回答一下,以防像我一样的人遇到它。ORM已经发展了很长时间。其中一些实际上给你最好的两个世界:使开发更加高效并保持性能。
看看SQL Data(http://sqldata.codeplex.com)。它是一个非常轻量级的C# ORM,涵盖了所有基础知识。
FYI,我是SQL Data的作者。

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