【问题标题】:Using an ORM or plain SQL? [closed]使用 ORM 还是普通 SQL? [关闭]
【发布时间】:2010-10-04 10:26:09
【问题描述】:

对于我开发的一些应用程序(后来忘记了),我一直在编写简单的 SQL,主要用于 MySQL。虽然我在 python 中使用过 ORM,比如SQLAlchemy,但我并没有坚持太久。通常是文档或复杂性(在我看来)阻碍了我。

我是这样看的:使用 ORM 来实现可移植性,如果只是要使用一种类型的数据库,则使用纯 SQL。在开发需要数据库支持的应用程序时,我真的在寻找有关何时使用 ORM 或 SQL 的建议。

仔细想想,与使用 ORM 相比,使用轻量级包装器来处理数据库不一致要好得多。

【问题讨论】:

  • 标准化、安全性、可维护性、语言抽象、DRY等
  • ORM 的性能可以接近 SQL,这取决于您是否正确使用它并设置正确...请参阅 ho 以使 EF6.x 快 5 倍:linkedin.com/pulse/…
  • 对于 ORM 架构和操作方法(要避免什么),这是我的另一个链接:linkedin.com/pulse/…
  • 对象-关系映射 (ORM) 在许多编程语言中已经非常流行,并且是 SQL 的最佳替代方案之一。我从方法链风格中获得灵感,为我的 TRIADB 项目创建 CQL。 healis.eu/triadb/#latest-release
  • ORM 是不了解 SQL 或懒于编写 SQL 的借口。如果您不了解 SQL,请使用 ORM。但是不要浪费你的时间认为你在使用 ORM 时会做更少的工作,那将是一个错误的信念。可移植性同意...如果您的项目需要跨数据库的可移植性,请使用 ORM。速度?一直使用 SQL,因为 ORM 并不快。可靠性 我会坚持使用 SQL。安全性,至少两者都没有什么可担心的。最重要的是,这取决于您的项目要求,尤其是在数据库可移植性方面。

标签: sql language-agnostic orm


【解决方案1】:

任何可敬的设计都需要对数据库进行一些抽象,以处理阻抗不匹配。但我希望最简单的第一步(并且对于大多数情况来说已经足够了)是 DAL,而不是重量级的 ORM。您唯一的选择不是那些处于光谱末端的选项。


针对要求我描述如何区分 DAL 和 ORM 的评论进行编辑:

DAL 是您自己编写的,可能从一个简单地封装表并将其字段映射到属性的类开始。 ORM 是您不为从 dbms 模式的其他属性(主要是 PK 和 FK)推断出的抽象机制编写的代码。 (在这里您可以了解自动抽象是否开始泄漏。我更愿意故意通知他们,但这可能只是我个人的偏好)。

【讨论】:

  • 什么是 DAL 和什么是 ORM?
  • 那么,如果您是 ORM 的作者,您的 ORM 会自动转回 DAL 吗? :)
  • DAL = 持久层和 ORM 是您在 DAL 内部使用的一种工具,用于对数据存储执行 CRUD 操作。
【解决方案2】:

作为一个花费大量时间使用 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 方法的批评之一是,您最终会得到所有这些 VO(值对象)或 DTO(数据传输对象),它们仅由一个查询使用。这被吹捧为 ORM 的一个优势,因为你摆脱了它。

问题是 ORM 不会解决这些问题,它们只是向上移动到表示层。您无需为查询创建 VO/DTO,而是创建自定义表示对象,通常为每个视图创建一个。这如何更好?恕我直言,它不是。

我已经在ORM or SQL: Are we there yet? 中写过这个。

这些天我选择的持久性技术(在 Java 中)是 ibatis。它是一个围绕 SQL 的非常薄的包装器,它完成了 JPA 可以做的 90% 以上的事情(它甚至可以做关系的延迟加载,尽管它没有很好的文档记录)但开销要少得多(就复杂性和实际代码而言)。

这是去年在我正在编写的 GWT 应用程序中出现的。服务实现中从 EclipseLink 到表示对象的大量转换。如果我们使用的是 ibatis,那么使用 ibatis 创建适当的对象然后在堆栈中上下传递它们会简单得多。一些纯粹主义者可能会认为这是 Bad™。也许是这样(理论上),但我告诉你:它会导致更简单的代码、更简单的堆栈和更高的生产力。

【讨论】:

  • 我受到启发,发布了另一个(尽管是社区维基)问题,只是为了收集有关此类事情的资源。关于最后一段:我喜欢简单。可能太多了。
  • iBATIS 很棒,但也许你想试试 jOOQ:jooq.sourceforge.net。由于您提到的 6 个原因,它的主要重点正是与 SQL 保持密切联系。
  • 第 3 点 +1。许多人认为使用 ORM 会使您无法彻底了解 SQL。问题是,一旦你可以/学会使用 SQL 做体操,你可能会发现自己很快就会远离 ORM。
  • 所以,现在是 2013 年底,众所周知,没有什么比“旧事实”更容易误导的了——请问您的观点是否仍然相同?如果没有,如果您能写一篇博文/相应地更新您的答案,那就太好了。
  • var 在 .NET 中不会产生动态类型,带有 dynamic 关键字的变量是 .NET 中的动态类型。 var 仍然是静态类型。见stackoverflow.com/questions/961581/…
【解决方案3】:

我说 Reads 的普通 SQL,CUD 的 ORM。

性能是我一直关心的问题,尤其是在 Web 应用程序中,但代码的可维护性和可读性也是如此。为了解决这些问题,我写了SqlBuilder

【讨论】:

  • 什么是 CUD?我找不到定义。
  • @KimchiMan CRUD 没有 R.
  • CUD - 创建、更新、删除。
【解决方案4】:

我想在“有一个中间立场!”的回复中加入我的声音。

对于应用程序程序员来说,SQL 是您可能想要控制的东西和您几乎肯定不想被打扰控制的东西的混合体。

我一直想要的是一个层(称为 DAL、ORM 或 micro-ORM,我不介意哪个)负责完全可预测的决策(如何拼写 SQL 关键字,括号去吧,什么时候发明列别名,为一个包含两个浮点数和一个整数的类创建什么列......),同时让我负责 SQL 的更高级别方面,即如何安排 JOIN、服务器-边计算、DISTINCT、GROUP BY、标量子查询等。

所以我写了一些这样的东西:http://quince-lib.com/

它适用于 C++:我不知道这是否是您使用的语言,但无论如何,看看这种“中间立场”的样子可能会很有趣。

【讨论】:

    【解决方案5】:

    我知道这个问题已经很老了,但我想我会发布一个答案,以防有人像我一样遇到它。 ORM 已经走过了漫长的道路。其中一些实际上为您提供了两全其美的优势:提高开发效率并保持性能。

    查看 SQL 数据 (http://sqldata.codeplex.com)。它是一个非常轻量级的 C# ORM,涵盖了所有基础。

    仅供参考,我是 SQL Data 的作者。

    【讨论】:

      【解决方案6】:

      使用类似 SQL 的 ORM,但提供编译时检查和类型安全。喜欢我最喜欢的:Data Knowledge Objects(披露:我写的)

      例如:

      for (Bug bug : Bug.ALL.limit(100)) {
        int id = bug.getId();
        String title = bug.getTitle();
        System.out.println(id +" "+ title);
      }
      

      完全流式传输。易于设置(无需定义映射 - 读取您现有的模式)。支持连接、事务、内部查询、聚合等。几乎可以在 SQL 中执行任何操作。并且已经从庞大的数据集(金融时间序列)一直到微不足道的(Android)证明。

      【讨论】:

      • 您的 IDE 也可以直接提供此类静态检查(IDEA 知道数据库结构,只要您告诉它数据库在哪里/DDL 文件在哪里,它就可以进行类型检查/关系检查/等在您的 SQL 查询/程序/任何内容中)
      • 这很有用。它可以作为构建/ CI 步骤的一部分吗?它如何对 sql 与其他字符串进行分类?它可以处理字符串操作,还是只处理字符串常量?
      • 我会被 abBlock 阻止,但是 IntelliJ 像任何其他语言一样解析 SQL jetbrains.com/datagrip/features 所以可以将它集成到 CI/CD/build 中(也许通过要求 IJ 团队隔离 SQL 解析代码? 也许 Sonar 已经有了这样的解析器)。解析带来了数据类型,因此您可以对它们添加检查(我已经使用自定义插件这样做了),或者像“JOIN 列是否有 FK?索引?”之类的检查。等等。这些将是对原生 IJ 的 SQL 检查的巧妙改进
      【解决方案7】:

      每个工具都有其目的和愿景。我已经创建了 http://www.jooq.org/ 来完全满足您的需求,尽管 iBatis 对您来说可能也是一个很好的解决方案。

      jOOQ 具有基本的 ORM 功能,但它主要关注我猜大多数开发人员最需要的东西,当他们试图找到最适合他们需求的 ORM 时:

      • 代码生成
      • 变量绑定(这在 JDBC 中很痛苦)
      • SQL 语法抽象(防止语法错误)

      但它们往往太过分了并且提供了如此多的抽象,您不会认为它们是针对 RDBMS 运行的。另一方面,您选择 RDBMS 正是因为

      • 它是一个强大的数据源
      • SQL 可以做很多好的、高性能的事情(嵌套选择、联合、复杂连接等)。通常 ORM 不能做这些事情。
      • 您可以自己处理事务和会话
      • 您有 UDT 和存储过程

      jOOQ 正好解决了这些问题。它的性能与 JDBC 一样好,但没有痛苦。

      【讨论】:

        【解决方案8】:

        ORM 有一些不错的功能。他们可以处理将数据库列复制到对象字段的大部分繁琐工作。他们通常处理将语言的日期和时间类型转换为适当的数据库类型。它们通常通过实例化嵌套对象来非常优雅地处理一对多关系。我发现如果您在设计数据库时考虑到 ORM 的优点和缺点,它可以节省大量将数据输入和输出数据库的工作。 (如果你需要映射它们,你会想知道它如何处理多态性和多对多关系。正是这两个域提供了大部分的“阻抗不匹配”,使得一些人将 ORM 称为“计算机科学的越南” .)

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

        对于报告繁重的应用程序,或者每个请求处理大量数据库行的应用程序,ORM 税要重得多,并且它们所做的缓存变成了巨大的、无用的内存占用负担。在这种情况下,简单的 SQL 映射(LinQ 或 iBatis)或在精简 DAL 中手动编码的 SQL 查询是可行的方法。

        我发现对于任何大型应用程序,您都会发现自己同时使用这两种方法。 (用于直接 CRUD 的 ORM 和用于报告的 SQL/thin DAL)。

        【讨论】:

        • 你能定义“每个请求的大量数据库行”吗?请:)
        • 那么我可以将 JPA 与 IBatis 集成吗?并让它们在同一个事务中工作?
        • 另一个似乎没人讨论的考虑是基本的状态管理。整个框架堆栈(JSF、JPA 等)都基于 Java bean 的 get/set 方法。这是每个表、每个列的大量样板,并且......这是真正的反模式:只是将每个字段都公开,就好像它是公开的一样。实际上,在对象/表/行中的字段上使用 get/set 方法非常接近于违反信息隐藏和封装的每个租户。最后,回到状态管理……不变性选项在哪里?可以或应该允许半集对象吗?大多数人都没有选择。
        • 我想磨练并特别同意此答案中的关键声明。 “对于每个请求处理大量数据库行的应用程序,ORM 税要重得多”。 ORM 只适用于开发人员和维护,因为大多数开发人员并不擅长 SQL,但如果你真的在谈论性能,SQL 完全胜过它。
        • “大多数开发人员都不擅长 SQL”???我想说大多数开发人员不知道如何正确使用 LINQ、表达式树的强大功能以及一般的 ORM、代码生成和许多其他东西。但是不,我没有任何依据做出如此强烈的声明。
        【解决方案9】:

        没有“一刀切”的解决方案,对于“我应该使用 or/m 还是不使用? '。

        我会说:如果您必须编写一个非常“数据”的应用程序/工具,没有太多其他逻辑,那么我会使用纯 SQL,因为 SQL 是此类应用程序的特定领域语言.

        另一方面,如果我要编写一个包含大量“领域”逻辑的业务/企业应用程序,那么我会编写一个可以用代码表达这个领域的丰富类模型。在这种情况下,OR/M 映射器可能对成功执行此操作非常有帮助,因为它无需您掌握大量管道代码。

        【讨论】:

        • “没有‘一刀切’的解决方案”.. 应该有。
        【解决方案10】:

        让我的 ORM 真正发挥作用的关键是代码生成。我同意 ORM 路线在代码性能方面并不是最快的。但是当你有一个大中型团队时,数据库正在迅速变化,从数据库中重新生成类和映射的能力作为构建过程的一部分是非常值得一看的,尤其是当你使用 CI 时。所以你的代码可能不是最快的,但你的编码会是——我知道在大多数项目中我会采用哪个。

        我的建议是在 Schema 仍处于流动状态时使用 ORM 进行开发,使用分析来查找瓶颈,然后使用原始 Sql 调整需要它的区域。

        另一个想法是,如果以正确的方式使用 Hibernate 内置的缓存通常可以大幅提高性能。无需再返回数据库读取参考数据。

        【讨论】:

        • 完全是个人喜好问题。对我来说,代码生成是一个缺陷。
        • 阅读第二段....也许完整性也有用
        • 代码生成是更快完成某些任务的唯一方法。像所有工具一样,它可能很强大,也可能导致灾难。从技术上讲,所有语言都在生成其他类型的代码。
        【解决方案11】:

        是否使用框架的困境在现代软件开发场景中很常见。

        重要的是要了解每个框架或方法都有其优点和缺点 - 例如,根据我们的经验,我们发现 ORM 在处理事务(即插入/更新/删除操作)时很有用 - 但在获取具有复杂结果的数据,评估 ORM 工具的性能和有效性变得很重要。

        此外,重要的是要了解,选择框架或方法并实施其中的所有内容并不是强制性的。我们的意思是我们可以混合使用 ORM 和本地查询语言。许多 ORM 框架为原生 SQL 中的插件提供扩展点。我们应该尽量不要过度使用框架或方法。我们可以结合某些框架或方法,并提供合适的解决方案。

        您可以在插入、更新、删除、高并发版本控制时使用 ORM,并且可以使用 Native SQL 生成报告和长列表

        【讨论】:

        • 为什么 ORM 更适合高并发?
        【解决方案12】:

        我开发的一个应用程序是一个用 python 编写的 IRC 机器人。它使用的模块在单独的线程中运行,但我还没有想出在使用 sqlite 时处理线程的方法。不过,这对于单独的问题可能会更好。

        我真的应该改写标题实际问题。我以前从未真正使用过任何语言的 DAL。

        【讨论】:

        • 好吧,我认为你应该这样做。到处都是原始 SQL 非常可恶。
        • 嗯,是的。有一个我不时破解的论坛软件,到处都有 tons 的 mysql_query() 和 mysql_result()。太疯狂了。
        • 你说的这个“应用”是什么?
        • 有趣的是,这个问题是通过 irc bot 应用程序提出的并变成了它的样子(一个非常有用的指南)!一个 irc 机器人应用程序处于规模的一端,而一个拥有 50-100 多个表的复杂连接和数百万行数据的应用程序处于规模的另一端。我敢说,当谈到规模的“irc bot 应用程序”时,它几乎不重要。
        【解决方案13】:

        ORM 不仅仅是可移植性(就此而言,即使使用 ORM 也很难实现)。它为您提供的基本上是对持久存储的抽象层,当 ORM 工具使您免于编写样板 SQL 查询(通过 PK 或谓词、插入、更新和删除进行选择)并让您专注于问题域时。

        【讨论】:

        • 我正在考虑更接近跨数据库风格的可移植性。我不应该在深夜发布问题。
        • 这正是我所说的:即使是最基本的场景也可能会在不同的 DBMS 中出现错误 - 例如,对 NULL 的不同处理。
        • ORM 为您提供了对象之间关系的抽象层,但对于您提到的样板查询而言,并没有太大的优势。在 JDBC 应用程序中,您可以使用抽象超类或实用程序类中的少量代码编写这些类型的查询。无需为每个新表重复样板。
        猜你喜欢
        • 1970-01-01
        • 2013-11-25
        • 2014-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多