不限编程语言,你认为哪个 ORM 最好用?

2022 年 7 月 15 日
 yodhcn

用过一段 nodejs 的 prisma,CURD 确实很爽,但麻烦一点的查询就只能手写 SQL 了,因为 prisma 这个框架不提供底层的 query builder ,最后还是换回了 knex 这个 query builder 一把梭。

我想了解一下其它编程语言都有哪些好用的 ORM ,哪些 feature 让你选择这个 ORM ?

听说过 laravel 的 Eloquent 很好用,但不知道 php 现在 Type Hints 发展的怎么样,有没有像 typescript 一样被广泛应用,因为现在大部分常用的 npm 包都有官方或第三方 typescript 类型注释支持。

21711 次点击
所在节点    程序员
144 条回复
msg7086
2022 年 7 月 16 日
接上面一楼,说说 Rails 的 ORM 。
我用过的 ORM 不多,十几年前还在写 PHP 的时候给公司写过一个简易的称不上 ORM 的查询工具,用来简化一些代码。这里只能简单聊一聊皮毛。

Rails 的 ORM 查询器有很多的黑科技。比如说你从表里查数据的时候写条件,在代码里可以把多个条件拆开成多个语句去写,而不是一次写完整。所以你可以这样写:
p = Post.all
p = p.where(user_id: uid) if uid != nil
p = p.where(forum_id: fid) if fid != nil
p = p.where(created_by: (7.days.ago)..) if show_recent?
p = p.order(order_by) if order_by != nil
@ posts = p.page(page)
另外,从例子里你也可以看出,不仅有 7.days.ago 这样的表达( 7.days 是 Integer 上重载的一个方法,返回一个 Duration ; 7.days.ago 是 Duration 上的一个方法,返回一个带时区的 Time ),有 x.. 这样从 x 到无穷大的 Range 类型,还有 Relation.page 这样利用 kaminari 插件实现的无痛分页组件。

对于不熟悉这套系统的人来说,只能是看得一头雾水吧。但是如果你很喜欢这种「 black magic 」式的设计,你就会觉得特别爽……

就说这些吧,说得多了会被人骂的。
yodhcn
2022 年 7 月 16 日
@msg7086 #119 感谢老哥用心的回复!

我可能搞混了“强类型”和“静态类型”的概念,我还是认为像 TypeScript 这种看一眼函数签名,就能清楚返回值类型,更有安全感。

而且我也看了 ruby 官网的入门教程,ruby 的类都可以在运行时动态修改,这么灵活的语言,在维护项目代码时不会麻烦吗?不禁让人产生这样的疑问,因为我体验过 python ,过于自由的编程语言,代码质量主要看人,不同开发者写的代码一个天上一个地下,两极分化严重。

另外我也注意到了 https://github.com/ruby/rbs 与 TypeScript 以及 Python 的 Type Hint 类似,都是通过类型注释实现接近静态类型的体验。TypeScript 现在用的人很多,而且提供了用 .d.ts 声明文件这一方式,让老旧的包也能用上类型注释。但现在的 ruby 社区好像还没怎么普及 rbs 的样子。
msg7086
2022 年 7 月 16 日
@yodhcn Ruby 和 Python 的巨大区别就是 Python 环境更期待人们有较少的自由,而 Ruby 则给予人们很多的自由。Python 的理念是做一种事应该只有一种最好的做法,而 Ruby 的理念是做一种事应该有很多种做法。Python 其实已经很「死板」了,用 Python 搞 black magic 我的确也见过(我之前重写我司的一个项目就碰到了一个放飞自我的 Python 模块),但是很少很少,基本可以保证写了三五个月的新手和写了三五年的老手能写出差不太多的代码。Ruby 就没这个可能了,写三年和写五年都能差很多,甚至你拿到自己两三年前写的代码都能恨不得重写一遍,因为你现在已经可以用更 magic 的做法来重构了。(在我自己身上已经发生过很多次了,越写越花哨,越写越 magic……

Rails 现在就是通过半强迫性地促使你写测试代码来保证可维护性。内部实现可以一团糟,但是有了测试以后,重构甚至重写都不是大事,不满意的地方以后再迭代就行了。

Ruby 的 Type Hinting 是个新玩意儿,我自己都很少用,一个很大的原因是需求太少,至少我自己很少会遇到 Type Hinting 能改善代码质量或者阅读体验的场景。有很多时候如果需要有 Type Hinting 才能搞懂的话,可能是代码结构设计有问题了,我个人觉得。
ecnelises
2022 年 7 月 17 日
Rails 的 ActiveRecord 基本介绍在这里: https://ruby-china.github.io/rails-guides/active_record_querying.html

其实 Rails 这套 ORM 也不是只能在 Ruby 这种高度灵活的动态语言里才能实现,至少大部分我感觉是可以在元编程和静态反射能力强的静态语言里实现的(比如 Rust ?)。其实写 Rails 的时候感觉这套理念也没有太出奇(毕竟 Active Record 本身其实也是一个设计模式的名字),但用其他框架的时候才意识到原来 ORM 可以做得这么水,哈哈哈。

其实 ORM 的一大痛点在于它的对象模型和 SQL 基于的关系模型并不兼容。如果放弃用面向对象建模数据库,而是在一个类型系统足够强大的强类型语言里( C++模版?)对关系模型建模,也许是条好出路。
mmdsun
2022 年 7 月 17 日
不用说,肯定是 Entity Framework
无论是创建表,还是查询 API 设计都十分优雅。还有强大的异步支持。

https://docs.microsoft.com/zh-cn/ef/ef6/querying/
stevefan1999
2022 年 7 月 17 日
最具生產力: EF Core

最創新: Exposed https://github.com/JetBrains/Exposed
MintZX
2022 年 7 月 17 日
ActiveRecord 啊
ChangJingli
2022 年 7 月 17 日
Sequelize
tinyuu
2022 年 7 月 17 日
java ebean
molika
2022 年 7 月 17 日
hugsql
alinwu05
2022 年 7 月 17 日
yii2 的 ActivateRecord 应该是借鉴 Rails , 实在是太好用了,换语言都缺乏动力!
BlindingDark
2022 年 7 月 17 日
Elixir Ecto!
fqzz
2022 年 7 月 17 日
没见过比 SQLAlchemy 更容易写复杂查询的 ORM
PopRain
2022 年 7 月 17 日
EF
nine
2022 年 7 月 17 日
@yodhcn

静态类型检测可以给你安全感,也只是安全感而已。但是无论是静态,还是动态,该考虑的事情一个也跑不了。考虑不到,最后还是要手工处理。

而静态会让人更死板,更繁琐。动态就简单,比如我查一个 status_id

@regulates = @regulates.where(status_id: params[:status_id])

api 可以传过来一个 int 就是
@regulates.where(status_id: 1)
也可以直接传一个数组,就是
@regulates.where(status_id: [1,2,3])

这都是一些基础用法。

如果想连表查询就
```ruby
@regulates = @regulates.joins(:business).where("businesses.name like '%#{@business_name}%'")
```

想连一堆的表,输出 json 就直接
```ruby
@regulates = @regulates.as_json({
include: {
market: {only: nil , methods: :area_name } ,
business_owner: {except: :password} ,
submit_serv_user: {only: :username} ,
serv_depart: {methods: :parent_tree_names} ,
} ,
methods: [:status , :regulate_show_status , :alarm_status ,:regulate_times]
})
render json: @regulates
```
inlcude 是关联模型,直接连表查数据。
only 是只查某些字段。
except 是除了某些字段,其他都查出来。
method 是直接调模型的方法,可以用来组合复杂的数据,而不是只查表里的内容。
而且可以 include 的模型再 include ,无限套娃(当然需要动动脑子,考虑一下性能问题)
是不是比其他 ORM 强出几(十)条街?


你真正用了 Rails 就会发现,哪哪都能找出甩其他语言框架几条街特性。
比如你要 post 一个时间到数据库,你可以'2012/12/12' 也可以'2012 12-12' 也可以 '2012/12/12 18:00:00',随便什么样的格式都可以。这种细节数不胜数。
你和别人的表结构同步,就需要麻烦一点写 migration ,但是这相当于把数据库表结构严格做了版本控制了。
而部署上,我目前都不用 docker ,因为 Rails 应用本身就是一键部署的。
1 git 提交代码
2 运行 cap production deploy
3 等待部署结束


Ruby 在中国不火,主要原因是 3 点
1 互联网创业大潮时期,在中国 PHP 太火,挤掉市场份额。
2 然后是平台限定,当时 Windows 下 Ruby 基本没法写,基本都是 Mac ,团队硬件成本投入太高,所以也才有各种仿制品。
3 Ruby 开发效率太高,当你产出是别人几倍拿的还是同样工资,而维护的业务逻辑大量,要把人累死。投资人不懂,只会觉得是理所应当。


所以除了真正打算自己赚钱的创业项目,或者写给自己用的项目,基本很少有用 Ruby 的。


我最早写的是 PHP ,2010 年自己写了一个 PHP 框架,抄袭对象的是某个抄 Rails 的 PHP 框架 QeePHP ,抄他是因为他不维护了。同一时期有大火的 Django 、YII ,Django 压根没考虑数据库表结构版本维护,YII 是比较割裂的,migration 做的很初级。我自己的框架想到了 migration 的思路,但是实在不想去做这些底层的东西,因为我的目标是开发赚钱的应用。后来的 Laravel 基本就是 Rails 的强势 copy ,但是用肯定要用更成熟的原版。

因为 Ruby 是动态语言,所以写 Rails 你应该写一些测试,来保证程序的健康。但是静态语言也应该写测试啊。

你又担心,多人协同开发,写的代码相互看不懂。但有没有想过,Ruby 和 Rails 的效率可能让你根本不需要那么多人?我现在手里项目 56 张表,10 万多行代码,前后端一个人。

而且 Ruby 虽然可以 magic ,但并没有规定你不能用朴素的方式来书写代码。
相反你可以规定,不让自己 /员工用花里胡哨的方法写代码。因为写的 99%代码都是业务,想花哨也花哨不起来,该是 if else 他就得是 if else 。

而事实上我们正常只有 hack 第三方包,和写一些工具类的时候才会用元编程。其他代码都是一板一眼的。

所以,你会发现,为什么 Rails 国内这么小众,但是一提牛逼的框架尤其是 ORM ,都要说 Rails 。因为他就是强啊。
自己研究代码,强烈建议玩玩 Rails 。自己写项目,强烈推荐使用 Rails 。

PS:只用来做后端哦,orm 和 API 。Rails 默认前端那套东西很不成熟。老版本配套的 webpacker ( webpack+周边)倒是可以,我自己用 webpacker 和 vue 整合了一套实践还是很不错的,但是还没有时间能分享出来。

完了,变成安利贴了。逃
StarkWhite
2022 年 7 月 17 日
mybatis 应该算不上 orm 库。。。
haoliang
2022 年 7 月 18 日
@msg7086 @nine 抱歉直接 @了,能捎带介绍下 ruby 界的 async 生态吗? rails 对 async 的支持如何或者 rails 需要 async 吗?
msg7086
2022 年 7 月 18 日
@haoliang 不太清楚你说的 async 是哪种 async ?并行计算的话 Ruby 这边应该更偏向于用 Parallel 吧。

@nine 你这怎么突然就安利起来了→_→
haoliang
2022 年 7 月 18 日
@msg7086 基于 coroutine 的 concurrency 这种, 针对 io 密集型应用,python 、go 、rust 、zig 这些语言中提供的;找到了这个 actor 模型的得星比较多的 concurrent-ruby ,基于线程池
> Ruby has a very different runtime so true goroutines are not possible. Instead, a Channel.go method is provided for running a block asynchronously, multiplexed onto a special thread pool reserved just for Channel operations.
不负责任地说,ruby 是不是在这块掉队了?
msg7086
2022 年 7 月 19 日
@haoliang 可以看看 Fiber 和 EventMachine 。
async 没有特别激进地实现,我猜是因为 EventMachine 早就已经扎根于各种类库里了,没有动力去实现了吧。就算要用,线程池也已经很方便了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://v2ex.ih06.com/t/866413

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX