• 我的订阅
  • 科技

ORM 是“反模式” 吗?

类别:科技 发布时间:2023-07-05 21:00:00 来源:CSDN

【编者按】ORM(对象关系映射)在软件开发中受到争议,常见批评包括违反 SOLID 原则和效率低,但实际问题在于透明性和调试困难。如果能够正确使用 ORM ,那么它的执行效率也会很高,但很多开发者往往过度依赖主机语言而没有充分利用 ORM 的原生 SQL 功能。

原文链接:https://github.com/getlago/lago/wiki/Is-ORM-still-an-%27anti-pattern%27%3F

作者 | lago 译者| 明明如月

责编 | 夏萌

出品 | CSDN(ID:CSDNnews)

前言

对象关系映射(ORM)常常是软件开发者热议的焦点。

你可以在网络上发现大量文章唱反调:“ORM 是反模式,它们只是创业公司的小玩具,整体来说弊大于利。” 这其实有些过分夸大其词。ORM 并非全无用处。正如软件领域里的技术一样,完美的 ORM 并不存在。然而,对 ORM 的批评也在意料之中——若是两年前,我可能也会全盘接受这些刻板印象。我也曾有过类似 "你是说 ORM 把服务器的内存耗尽了?" 的遭遇。

但是,实际情况是 ORM 框架被误用的例子要比被滥用的例子还要多。

这篇试图辩护 ‘ORM 其实并没有那么糟糕’ 的文章,源自我们在 Lago 公司因为 ORM 遭遇的一次不愉快经历。这次经历使我们开始对我们对 Ruby on Rails ORM,即 Active Record 的依赖产生疑问。如果要为这篇文章取一个更具吸引力的标题的话,可能会是 “ORM 的确有些问题”。但在深入思考这个问题后,我们认为 ORM 框架并不糟。它们只是一种抽象工具,有其优点和缺点——它们抽象化了一些可见性,偶尔会导致一些性能损失。仅此而已。

今天,我们就来深入探讨 ORM,分析常见的批评和聊聊真正的问题所在。

两种范式的对比

我们从一个简单的问题开始:ORM 和数据库遵循两种不同的范式。

ORM 创建对象(这就是 O 的来源)。对象就像有向图——节点指向其他节点,但并不一定互相指向。而相反,数据库的关系表包含的数据总是通过共享键(也就是无向图)进行双向链接。

从技术角度来看,ORM 可以通过强制实现双向指针来模拟无向图。但实际上,这样做并不容易;很多开发者最后可能得到的是缺少Posts 数组的 User 对象 或者Posts 数组实体缺少对同一 User对象的反向引用(但可能是一个克隆对象)。

ORM 是“反模式” 吗?

ORM 和关系数据库遵循两种不同的范式。例如,ORM 可能会将用户(User)的帖子(Post)以数组的形式返回,但不会在每个帖子(Post)中包含对 User(作者)的反向引用。

然而,这种范式的不匹配并非无法解决。ORM 和关系数据库最终都只是图形呈现;只是数据库只有非定向边。尽管这对 ORM 来说是一个学术上较有道理的批评,但 ORM 的真正问题其实更为“深刻”。

打破的原则

在深入讨论"细节问题"之前,我们首先讨论一些更基础的原则。关于 ORM 框架的常见抱怨之一就是它们违反了在软件设计课程中所教授的 SOLID 原则中的两条。如果你不了解 SOLID,它是一个首字母缩略词,代表了一些重要的设计原则。

单一职责原则(SRP)

ORM 被批评违反了单一职责原则(SRP)。SRP 要求一个类应该只有一个存在的目的。然而,ORM 并未达到这一标准。在较高的抽象层面上,它们处理了"所有的数据库事务",但这就好比创建一个处理"所有应用事务"的单一类。JohnoTheCoder 对此有一个非常好的解释:ORM (i) 创建了用于和数据库交互的类,(ii) 代表了一个记录,以及 (iii) 定义了关系。我还会进一步补充说,ORM (iv) 创建并执行了迁移。

除了你,还有其他人对这种针对 ORM 的语义上的批评感到不满。我也认为这种常见的反对 ORM 的论点有点模糊。毕竟,ORM 的主要工作就是弥补两种根本不同的数据范式之间的差距;它当然会打破一些原则。

关注点分离(SOC)

关注点分离(SOC)的思想和 SRP 相似,但是应用在不同的层面。SOC 规定,强调将不同的功能分离开来,使得每个模块可以独立地开发、测试和维护。然而,ORM 将数据库管理从后端转移到了数据库本身,违反了 SOC。但在如今 SOC 这种原则已经有些过时了。现在,基础设施组件和编码模式正在协同合作,以实现更好的性能(比如在 OLAP 数据库中的 CPU 聚合器)、更低的延迟(例如在前后端中间的边缘计算)以及更清晰的代码(例如使用 Monorepo 仓库代码管理模式)。

真正的问题

现在我们已经详细地讨论了这些"假"问题,让我们来看看 ORM 的真正面临的问题。ORM 框架采取较为守旧的方法。它们使用一种可预测和可重复的查询系统,这种系统本身并没有被优化或可视化。然而,ORM 框架的开发者们意识到了这一点;他们已经添加了许多功能来解决这些问题,自 Active Record 首次亮相以来,他们已经取得了巨大的进步。

效率

开发者普遍认为 ORM 框架性能不佳。

这种说法在很大程度上是不准确的。实际上,ORM 框架的效率通常要比许多开发者想象的要高。然而,由于依赖主机语言(例如 Java 或 Ruby)来组合数据过于简单,ORM 的使用确实引导了一些不良的开发实践。

例如,看一看这段使用 Java 扩展数据条目的效率低下的 TypeORM 代码:constauthorRepository = connection.getRepository(Author); constpostRepository = connection.getRepository(Post);

// Fetch all authors who belong to a certain companyconstauthors = awaitauthorRepository.find({ where: { company: 'Hooli'} });

// Loop through each author and update their posts separatelyfor( leti = 0; i < authors.length; i++) { constposts = awaitpostRepository.find({ where: { author: authors[i] } });

// Update each post separatelyfor( letj = 0; j < posts.length; j++) { posts[j].status = 'archived'; awaitpostRepository.save(posts[j]); }}

相反,开发者应该使用 TypeORM 的内置功能构造单个查询:constpostRepository = connection.getRepository(Post); awaitpostRepository .createQueryBuilder.update(Post). set({ status: 'archived'}) . where( "authorId IN (SELECT id FROM author WHERE company = :company)", { company: 'Hooli'}) .execute;

在 Lago 的对账单 SQL 重构中有一个很好的例子。Active Record 的问题与其可见性有关(下面会详细讨论)。我们的 ORM 和原生 SQL 查询的性能相当。由于我们大量使用了 Active Record 的数据联接功能,我们的查询已经过优化:InvoiceSubion.joins( 'INNER JOIN subions AS sub ON invoice_subions.subion_id = sub.id') .joins( 'INNER JOIN customers AS cus ON sub.customer_id = cus.id') .joins( 'INNER JOIN organizations AS org ON cus.organization_id = org.id') .where( "invoice_subions.properties->>'timestamp' IS NOT NULL") .where("DATE( #{Arel.sql(timestamp_condition)}) = DATE( #{today_shift_sql( customer:'cus', organization:'org')} )" , today,).recurring.group( :subion_id) .select( 'invoice_subions.subion_id, COUNT(invoice_subions.id) AS invoiced_count') .to_sql

它被下面这个重写的原生 SQL 所替代:SELECTinvoice_subions.subion_id,COUNT(invoice_subions.id) ASinvoiced_count FROMinvoice_subions INNERJOINsubions ASsub ONinvoice_subions.subion_id = sub.id INNERJOINcustomers AScus ONsub.customer_id = cus.id INNERJOINorganizations ASorg ONcus.organization_id = org.id WHEREinvoice_subions.recurring = 't'ANDinvoice_subions.properties->> 'timestamp'ISNOTNULLANDDATE( (-- TODO:A migration to unify type of the timestamp property must performed CASEWHENinvoice_subions.properties->> 'timestamp'~ '^[0-9\.]+$'THEN-- Timestamp is

storedasan integerto_timestamp((invoice_subions.properties->> 'timestamp'):: integer)::timestamptz ELSE-- Timestamp is stored as a string representing a datetime(invoice_subions.properties->> 'timestamp')::timestamptz END) #{at_time_zone(customer: 'cus', organization: 'org')}) = DATE(:today #{at_time_zone(customer: 'cus', organization: 'org')})GROUPBYinvoice_subions.subion_id

不过,请别误会,相比于原生 SQL 查询,ORM 并非总是那么高效。在通常情况下,它们可能稍显低效,而在某些特定情况下,可能会变得非常低效。

第一个问题是,有时 ORM 在将查询转换为对象时会产生大量的计算开销,TypeORM 就是这个问题的罪魁祸首。

第二个问题是 ORM 有时会通过循环遍历一对多或多对多的关系来对数据库进行多次请求。这就是所谓的 N+1 问题(1个原始查询 + N个子查询)。例如,下面这个 Prisma 查询会为每一条评论都进行一次新的数据库请求!

首个问题是 ORM 有时在将查询转化为对象时会产生大量的计算开销(TypeORM 是此问题的主要罪犯)。

第二个问题是 ORM 有时会通过循环遍历一对多或多对多的关系,导致对数据库的多次请求。这就是所谓的 N+1 问题(1个原始查询 + N个子查询)。例如,下面的 Prisma 查询会为每一条评论都进行一次新的数据库请求!{users(take: 3) { idnameposts(take: 3) { idtextcomments(take: 5) { idtext}}}}

使用 ORM 框架经常会遇到 N + 1 问题。然而,它通常可以通过使用 data loaders 来处理,这些 data loaders 将查询合并为两个查询,而不是 N + 1。因此,就像大多数其他常见的 ORM “问题”一样,N+1 场景通常可以通过充分利用 ORM 的功能集来避免。

透明性

ORM 的主要问题在于透明性。由于 ORM 本质上是查询生成器,因此除了明显的场景(如错误的原始类型)之外,它们并不是最终的错误传递者。相反,ORM 需要处理返回的 SQL 错误并将其解释给用户。

在这方面,Active Record 遇到了挑战,这也是我们重构我们的计费订阅查询的原因。每当我们得到未预期的结果时,我们就需要检查生成的 SQL 查询,再次运行它,然后将 SQL 错误转化为 Active Record 的修改。这个反复的过程背离了 Active Record 的设计初衷,即避免直接与 SQL 数据库进行交互。

结语

实际上,并不能说 ORM 本身有什么问题。当我们正确地使用它们时,它们几乎可以和原生 SQL 一样高效。然而,遗憾的是,开发者们常常错误使用ORM,可能过分依靠主机语言的逻辑结构来创建数据结构,却没有充分利用 ORM 提供的与原生 SQL 类似的特点。

然而,ORM 在透明性和调试方面确实存在问题——这正是我们在 Lago 遇到的情况。当一个复杂的查询给开发者带来困扰时,将其转换为原生 SQL 查询可能是一个好的投资。幸运的是,大多数 ORM 本身也支持 SQL 查询的执行。

▶腾讯回应与Meta VR头显合作传闻;美国考虑限制中国用户使用美国云计算服务;ChatGPT暂停联网测试|极客头条

▶ 2023 年嵌入式开发现状:Linux、FreeRTOS位居榜首,专有软件代码复用更常见!

▶ 突发!ChatGPT 紧急暂停 Bing 集成,下线搜索功能 返回搜狐,查看更多

责任编辑:

以上内容为资讯信息快照,由td.fyun.cc爬虫进行采集并收录,本站未对信息做任何修改,信息内容不代表本站立场。

快照生成时间:2023-07-05 23:45:02

本站信息快照查询为非营利公共服务,如有侵权请联系我们进行删除。

信息原文地址:

数据不是微服务
...题的可信度降低。此外,它还 显著增加了花费。云分析数据库的定价模型主要基于查询操作的执行过程中所消耗的计算资源量。由于复杂的查询需要更多的资源,它们将导致每次查询的成本增加和
2023-07-13 19:00:00
oceanbase的一体化数据库发展历程
...把简单留给客户,复杂留给自己’,要做的事情是用一个数据库解决80%的问题,把OLTP的能力跟OLAP的能力融合在一起,能够处理复杂查询和简单查询,并且可以支持任意规模的数据量
2023-12-24 11:10:00
NFTScan x TiDB 一栈式 HTAP 数据库为Web3业务提供毫秒级多维查询
...金融科技公司提供专业的 NFT API 数据服务。目前,NFTScan 数据库收录了 100万+ 个 NFT 合约地址
2023-02-25 10:00:00
让AI管理大型代码库,开发者福音 | 阿里/新加坡国立/西安交大
...西安交通大学的研究人员提出了CodexGraph,一个以代码图数据库为媒介,连接了语言模型和代码库的系统。研究团队在三个有代表性的学术benchmarks对CodexGraph进行了评测
2024-08-12 09:49:00
大幅提升压缩率和查询性能,青云科技云数据库PostgreSQL支持列存储
青云科技推出的云数据库 PostgreSQL(PostgreSQL on QingCloud)近期正式支持列存储!在 V2
2023-02-17 06:00:00
聚焦OLAP性能提升,火山引擎ByteHouse性能挑战赛圆满落幕
...外,其他影响不大,ByteHouse性能还是很强悍的”,来自某数据库产品开发者这样介绍到。不仅仅聚焦在技术层面的性能调优,更多来自泛互联网、能源等行业的开发者,也在探索将By
2024-05-14 15:05:00
...搜索引擎模式下,用户输入关键词后,搜索引擎通过索引数据库返回匹配结果。尽管这种模式已经足够应对大多数日常需求,但在面对复杂问题或模糊查询时往往显得力不从心。例如,当用户询问“
2025-01-13 07:15:00
YashanDB V23.2 LTS发版
...,YashanDBV23.2满足各类业务场景、尤其是核心生产场景对数据库系统的严苛要求,是支撑金融、能源、政务等关键行业核心系统的里程碑版本
2024-04-23 16:00:00
从“零”到“无穷大”,数据拓展无极限,持续引领Serverless 构建之路
...括用于存储的 Amazon S3、用于计算的 Amazon Lambda、以及用于数据库的 Amazon DynamoDB
2023-11-29 19:28:00
更多关于科技的资讯:
“山情海韵 创见未来”青岛市崂山区第三届文创设计大赛作品火爆征集中,入围即享孵化赋能!
鲁网1月9日讯在黄海之滨、崂山脚下,千年文脉与现代潮流在此交融共生。为推动中华优秀传统文化创造性转化、创新性发展,助力崂山文旅高质量发展提质增效
2026-01-09 16:45:00
安徽人文讲坛丨人工智能:向新、向深、向未来
大皖新闻讯 人工智能正以前所未有的速度重塑人类社会,成为驱动全球科技革命、产业变革与国家竞争的核心力量。从历史纵深审视
2026-01-11 17:59:00
“返本还原第四回——小尺幅版画作品展”在省美术馆展出
江南时报讯(记者 钱海盈)由江苏省美术馆主办、江苏版画院(水印版画材料与技术研究文化和旅游部重点实验室)承办的“返本还原第四回——小尺幅版画作品展”正在江苏省美术馆展出
2026-01-11 15:13:00
邦德激光SK高速款激光切管机,引领高效切割技术新高度
在金属管材加工中,如何让设备在真正“高速”运行时,仍能保持“高精度”与“高稳定性”?单纯提升单项参数往往顾此失彼。邦德激光认为
2026-01-11 15:47:00
OPC“最强大脑”集结 高德空间智能开发者大赛全国总决赛在苏州落幕
江南时报讯 “‘智能遛狗小助手’帮你制定更合理的遛狗计划。”“出门旅游,帮你一站式行程管理。”“无障碍出行,AI轻松识别障碍物
2026-01-11 16:26:00
合肥创新院举办“汽车+”产业科创企业路演
大皖新闻讯 为搭建产业与资本的高效对接桥梁,推动汽车科技创新成果转化,助力创新创业企业成长,近日,2026“汽车+”产业科创企业新年路演在合肥创新院举办
2026-01-11 16:52:00
在吉林,机器人也开始“卷”滑雪了!
当双足机器人蹒跚滑下雪道,当机械臂在-20℃寒风中精准投出雪球……这些充满未来感的画面,如今正在吉林的冰天雪地里真实上演
2026-01-11 15:05:00
中新经纬1月11日电 国家医保局网站11日发布《国家医疗保障局办公室关于开展个人医保云建设试点申报工作的通知》(以下简称《通知》
2026-01-11 12:46:00
都市快报讯 昨天上午,上城区同协路旁的西子智慧产业园迎来了一名特殊的“保安”——身高1.8米,通体银色金属光泽,走起路来
2026-01-11 08:22:00
人形机器人“入职”前的试炼场来了杭州日报讯 核心提示2025年被称为人形机器人量产元年,今年人形机器人步入量产关键期,如何让机器人理解并适应人类真实的世界
2026-01-11 08:22:00
河北新闻网讯(崔梦露)1月8日下午,平安产险唐山中心支公司通过抖音、微信视频号、平安好车主、平安好生活等四大线上平台,举办“开年迎好运家家有平安”直播活动
2026-01-09 21:11:00
在全球制造业向高质量升级的浪潮下,超高压技术正成为破解多行业性能瓶颈的关键支撑。作为全球超高压领域标杆企业,Quintus专注于超高压技术的研发与应用
2026-01-10 20:59:00
为规范互联网应用程序个人信息收集使用活动,保护个人信息权益,促进个人信息合理利用,根据《中华人民共和国网络安全法》《中华人民共和国个人信息保护法》《网络数据安全管理条例》等法律法规
2026-01-10 21:56:00
浙江造,红遍中国年!万事利春晚红围巾成“新年硬通货”
随着2026马年临近,一条承载马年春晚吉祥寓意的红围巾,正成为年末备受瞩目的新春佳礼。作为总台文创官方合作品牌,万事利丝绸匠心推出的“骐骥驰骋”系列围巾与丝巾
2026-01-10 21:02:00
摄像头自动识违规!雄安的这个工地很“智慧”
1月4日上午,雄安新区启动区中国中化大厦二期项目施工现场,一个安装在工地入口的摄像头缓缓转动。当镜头捕捉到两名未按规定穿戴反光背心的工人走向施工区域时
2026-01-10 17:01:00