Gopher 苦 ORM 久矣,发布个使用 rawsql 节省体力的包,使用姿势与标准库基本相同
初衷
这是前几天看到隔壁帖子吵得热闹,一时兴起写的,还没大规模测试,感兴趣的同学欢迎来 issue/pr 骚扰。
隔壁帖子: /t/859178
项目地址
github.com/lesismal/sqlw
示例代码
github.com/lesismal/sqlw_examples
特点:
非 ORM ,仍然是标准库的姿势使用 rawsql
简单封装了标准库 sql ,Query 相关的方法增加了一个参数来接收结果,不再需要自己处理 rows
简洁干净,目前只使用标准库,无三方依赖
直接上代码吧:
db, err := sqlw.Open("mysql", "test:email protected/sqlw_test", "db")
if err != nil {
log.Fatal(err)
}
var dst examples.ModelForTest
err = db.QueryRow(&dst, select * from sqlw_test.sqlw_test
)
if err != nil {
log.Fatal(err)
}
log.Printf("dst: %v", dst)
var dsts []*examples.ModelForTest
err = db.QueryRow(&dsts, select * from sqlw_test.sqlw_test
)
if err != nil {
log.Fatal(err)
}
log.Printf("dst: %v", dsts )
db.Prepare|sql.Stmt, db.Begin|Tx 也类似,保持了标准库的简洁基础上省去了自己遍历 rows 去 scan 的麻烦,代码量节省很多
二位看下这种可否节省些体力
也欢迎 gorm 大佬多多交流指导
顺便推下自己另外两个库,详见旧帖或者我的历史帖:www.hesudu.com/t/794435
#1
既有项目就 gorm 恶心着
未来项目公司的人闲着没事自己造轮子
架构思想从 go-pg 来
但是这种混杂着也不好看懂吧 大佬能出个类似 mybatis 的吗?
混杂是指 sql 和 sqlw 同时?还好吧,sqlw 把 DB 、Stmt 、Tx 这几个都包装了下,使用 db.Prepare/Begin 返回的是 sqlw 的 Stmt/Tx ,Query 相关的写起来就都简单了,我再考虑要不要增加个 Insert 、Update ,怎么设计能保持简单与优雅。
mybatis ,java 和 go 真的是繁冗与简洁的两个典型了,所以我估计不会去模仿任何一个 java 相关的方案了 /手动狗头 。。
有和 jmoiron/sqlx 的对比吗?
别的不说,就这一堆 err 看着就头大
#6
sqlx 我没太用,自己以前主要是使用标准库 rawsql 。sqlx 、gorm 、xorm 也都简单看过他们的文档教程、还是挺复杂的,相比于使用标准库 rawsql ,这些 sql 库学习成本更高、姿势有点多反倒需要去注意更多问题。
sql 的主要操作,就是增删改查,标准库的查询用 Query/QueryRow ,其他的用 Exec ,非常简洁明了,麻烦的地方就在于 Query 相关的 rows 到结果的 Scan 。
sqlw 把遍历 rows 去 Scan 这些省掉了,中间的一堆 if err 也省掉了。使用 rawsql 能让程序员保持对 sql 语句的功能、性能直观和敏感、减少或者避免 ORM 背后行为那些隐藏的细可能导致拼接出来的 sql 不是自己想要的、甚至出其他问题,而且虽然不是 ORM ,但是执行语句与结果映射到结构体一步到位,还是很省力了。
#7
go 完全避免 if err 是不太现实的,sqlw 帮助去减少了 for rows.Next{ rows.Scan }这些地方的很多 if err 。
另外,很多人对 if err 抱怨,却没去想过,go 这样使用 if err 让他们的代码更健壮了。相比于其他一些语言用异常处理错误,go 把错误、异常各管各的其实更清晰,只是很多人“先入为主”,用习惯了以前的某种姿势或者框架提供的方式、不想去改变成更合理的方式罢了。
不知道为啥,写 go 的人都哪么喜欢纯 sql ,企业级项目,维护 sql 真的不累吗?
不累,反而更轻松,因为可以直接把 sql 复制出来运行测试
可以可以,我以后新项目里试试,这比 ORM 可舒服太多了
相比非 sql 于业务代码,sql 占比挺小的,而且 ORM 里你看不到 sql 的逻辑。
编程语言排行榜里,sql 甚至还前十呢,排名在 golang 和很多流行的语言前面呢。用 ORM 来对待 sql ,即使是 java 那些很成熟的 ORM ,也让我觉得是对 sql 的侮辱——人家是门语言,你们却像嫖客一样上来对人家一顿胡搞猛搞、连自己做了啥都不知道,然后还说 ORM 真香,真是对自己对 sql 都不负责,妥妥的渣男。。。
正解! ORM 党多数都是 cv 程序员、性能和服务稳定性啥的都不怎么在乎
一顿暴论
怎么处理关联查询呢
#12
其实其他那些 ORM 比较完善的语言,在做那些数据量不大的对性能没啥要求的项目时还可以的,比如学校的管理系统,公司的 OA ,这些通常单表都很小的没啥压力,用习惯了 ORM 的同行开发效率还是蛮高的
#14
比如 http 服务一个 routter handler:
- 如果是只需要某个特定的表,直接用一些三方工具导出与表结构映射的 struct 拿来直接用就可以了,比如 model.User 结构体或者结构体数组拿来接收 Query 的结果
涉及多表联查的,我们通常 router handler 文件里单独声明 struct 用于接收 rawsql 的字段,其实并不麻烦,而且通常规范点的项目,除了数据库的 model ,c/s 交互也是有规范的协议格式的,所以不管是 json 还是 pb 还是其他、不管是否联查、声明一个结构体这一步多数时候本来就已经存在于流程中了
联查的实现是否会过于复杂,如果因为代码量过高又封装一个库出来,那不是当初不如直接用 ORM 库。然后还有个点就是这种模式上来得熟悉 sql ,join 之类的复杂的语句,别人 ORM 即使可能不太懂 sql 也能上来就用,出了问题然后再查询。再着是 sql 安全问题,ORM 库一般有封装解决这个问题,然后由社区抓漏洞,而如果纯 sql ,安全就得自己把握。
我没预料到兄弟你认为 ORM 比 rawsql 难、ORM 比 rawsql 更安全。关于这两点,我的感觉刚好跟你相反。
sql 与 ORM ,我觉得 老兄 #12 这段虽然很逗,但确实又很贴切: “人家是门语言,你们却像嫖客一样上来对人家一顿胡搞猛搞、连自己做了啥都不知道,然后还说 ORM 真香,真是对自己对 sql 都不负责,妥妥的渣男。。。”
话说现在 ORM/sql 库没看到有用泛型的呢,像 Query row 方法用泛型把类型传进去还可以省一个参数+省一行声明
比如 dst,err:=db.QueryRowexamples.ModelForTest
希望能增加一个工具方法 ,遇到 err 自动回滚, 签名为:
sqlw.Transaction() (err error) {}
就像高级语言你不用,非要去用 C 语言,追求所谓的更好颗粒的控制,最后却本事不够,反倒遇到各种坑,写出的东西问题多多
如果按这个逻辑,那 c 语言早该被消灭了才对。然而并不是所有业务都是简单的 curd 。
另一个话题,curder 的收入也是个很难突破的瓶颈,要提升实力和收入,怎么办?固守所谓的使用高级语言?当顶流量级项目来临需要应对性能问题自己无从下手只能看着别人出手?
不能要求并期望所有人都只做 curder 的。
go 事务的用法是 tx, err := db.Begin(),但创建 tx 后的执行多条语句也是由用户实现的、框架层不好去判断,正确的用法是创建 tx 成功后直接 defer tx.Rollback() 就可以了,后面如果 Commit 成功了、defer Rollback 执行时会失败但不影响正确的逻辑,defer tx.Rollback()还可以避免连接泄露,比如中途 panic 了、既没有 Commit 也没有 Rollback ,因为 tx 独占一个 sql 连接,只有 Commit 或 Rollback 后才会把连接放回连接池给其他地方用。正确用法参考:
github.com/lesismal/sqlw_examples/blob/main/tx/tx.go#L26
直接使用标准库同样也是应该这样子的,否则就存在泄露的隐患
#19
首先,这写法似乎并没有比 sqlw 现在的样子更省事呀,兄弟你看我的示例代码。
而且 go 的泛型不是万能的,泛型适合具有同一类别方法的类型比如数值类,都可以进行基础的数学运算,然后泛型里不同的类型实例也可以加减乘除,但是如果你的逻辑里包括用泛型的数学运算但是你实际传的确实一个结构体进来,就不行了。c++那种泛型方便是因为可以操作符重载可以把各种需要的 class/struct 实现同类方法( go 的接口能实现这种但是不能满足泛型的需求比如返回值的类型),而 go 没有操作符重载。对于未知类型类别的参数,sql 库里去做字段映射时,仍然是需要用 reflect 实现。
也可以做自动 Rollback ,sqlw.Tx 再把 sql.Tx 的 Exec 包装一道,然后 Query/Exec 里都先判断下 sql.Tx 执行是否有 err ,有就回滚。但是这样的话,与标准库用法又有差异了,用户外面如果也写了 if err rollback 倒是影响不大。
我先琢磨琢磨看要不要加自动回滚
前两天运行一个 python 程序,有部分报错,是正则匹配失败,就定位到出错点,try except 跳过去,打印了出错的匹配对象,然后把情况在 issue 反馈了下,这事给忘…
小弟在网全栈方向发展,是去学期 JS+Vue 那一套呢还是 Dart+Flutter 那一套呢?想听听客户端过来人的想法。 走了 JavaScript 这条路,感觉什么 E…
之前用 intel 的 macbook pro 的时候配备 2 个外置 4k 显示器。现在换了 M2 以后只能插一个。多余了一个显示器。 一直有玩游戏习惯。但是 Mac 最多玩…