• 我的订阅
  • 科技

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
更多关于科技的资讯:
实现从光芯片设计到模块封装量产每日商报讯 近日,位于拱墅区大运河数智未来城省级高新区核心区块的拱政工出【2025】6号地块标准厂房(一期)项目(以下称“芯速联高速硅光模块智能制造基地”)正式完工
2026-03-12 08:12:00
涂鸦智能:以开放平台与AI能力重塑宠物喂食器解决方案
宠物智能用品正从基础的“联网控制”迈向“主动关怀”的深度智能时代。传统宠物喂食器多限于手机App远程定时出粮,难以应对动态调整食量
2026-03-12 08:34:00
纵览原创|石家庄“装虾”线上线下冰火两重天:多家线下店铺称“没听说过”,有线上卖家已安装20多单
见习记者 朱荣琛“你养龙虾了吗?”成为近期网络热议的话题。最近,这款名为OpenClaw(网友俗称“龙虾”)的开源AI智能体工具迅速走红网络
2026-03-12 08:47:00
长白时评评论员 崔越然近期一款名为OpenClaw的开源人工智能体工具火遍全网,因其鲜红的图标被广大网民亲切地称为“龙虾”
2026-03-12 10:29:00
想尝鲜,又怕烫手 西湖边,数百人排队免费领养本地版“龙虾”
西湖边掀起“养虾”热潮。春夜渐凉,西湖边的“养虾”热潮,到昨晚8点仍没有退去。从中午开始,滨次元AI文旅驿站外的12号场地就排起了长队
2026-03-12 09:13:00
杭州日报讯 近日,在中国纺织信息中心浙江分中心·国际纺织时尚交流中心的组织下,涛搏纺织、凯亨纺织、卓伦纺织、优时进出口等10家绍兴柯桥纺织标杆企业组团亮相越南西贡会展中心
2026-03-12 07:11:00
杭州日报讯 “开年前两个月,订单量同比两位数增长,完成甚至突破预定生产目标,我们信心很足!”“产线满负荷运转,订单排得满满当当
2026-03-12 07:11:00
数字贸易平台海外版上线杭州日报讯 昨日,在2026年中国国际针织(春夏)博览会上,濮院毛衫数字贸易平台海外版正式上线,其专属海外品牌“PlinkX”同步亮相
2026-03-12 07:11:00
推动进口贸易与消费升级深度融合杭州日报讯 为深入推进高水平对外开放,释放中国超大规模市场红利,推动进口贸易与消费升级深度融合
2026-03-12 07:11:00
82岁老人换了新手机,但不熟悉操作,结果误开通多项收费业务。3月11日,老人收到扣费短信后向网格员求助,顺利取消了扣费服务
2026-03-11 17:36:00
中新经纬3月11日电 (董湘依)近年来,中国文化“新三样”(网文、网剧、网游)在海外迅速走红。全国两会期间,全国政协委员
2026-03-11 19:27:00
中新经纬3月11日电 题:“养龙虾”火出圈 ,算力变成稀缺资源作者 薛洪言 星图金融研究院常务副院长、苏商银行特约研究员2026年的早春
2026-03-11 19:28:00
河北新闻网讯(闫丽颖、唐福刚)近日,开滦股份范各庄矿聚焦“物理隔离+智能管控”核心需求,创新应用一套具备智能闭锁、声光报警
2026-03-11 19:51:00
中国联通eSIM尝鲜季再添新力
3月11日上午,中国联通eSIM尝鲜季——三星国内首款eSIM手机Galaxy S26系列首销仪式在西单北营业厅隆重举行
2026-03-11 14:14:00
大皖新闻讯 家里空调、冰箱等家电出故障,找维修却怕遇上“小病大修”“坐地起价”。别愁,专为安徽消费者打造的家电维修“放心平台”来了
2026-03-11 14:55:00