背景
去年慢慢开始接触了Go语言,在接触了一些Go的ORM框架之后,感觉用的不是很顺手,之后萌生了写一个Go版本的全自动ORM框架的想法。本来想着从零开始实现的,但是又觉得工作量挺大的,何不站在巨人肩膀上造一个呢。
于是我在Grom的基础上写了一个Gorm-plus,之后在掘金就发一篇简单的介绍。Go的ORM也太拉跨了吧,赶紧给他封装一下
这也引来了一些社区朋友的探讨
更多评论可以到这个文章查看 Go的ORM也太拉跨了吧,赶紧给他封装一下
我简单总结一下,主要为以下几个观点:
总的来说,社区朋友对这种基于Gorm做二次封装的ORM库看法不一,主要集中在Java和Golang思维差异、各orm框架比较、直接SQL与ORM的选择等方面。但整体而言还是以积极的态度探讨和建议的多。
疑惑
为什么Go到目前为止都没有一个全自动的ORM框架呢?
我搜集了一些回答
Go语言的设计哲学本身就追求简洁,反对过度的抽象和封装。全自动ORM框架更加复杂,与Go语言的定位不太配。
Go语言标准库自身就提供了database/sql等数据库访问相关功能,大多数项目直接使用标准库就能满足需求。
Go语言拥有很高的执行效率,引入全自动ORM的大量反射、缓存等机制,可能会带来显著的性能损失。
我的看法:
大规模用户场景下,数据库承载了主要的读写压力,成为性能瓶颈。
ORM框架的反射和缓存开销相比数据库操作时间其实是很少的。
Go语言本身执行效率很高,ORM框架的语言层面影响较小
开源的Go项目多由小团队或个人开发,不像Java有大公司或机构推动开发大型框架。
我的看法:
Go语言自身定位为简洁高效,对复杂框架需求不高,由小团队就可以满足需求。
Go生态发展时间不长,很多公司还在观望阶段,没有充分投入。
Go社区文化更注重简单直接,不追求框架化。
相对Java等语言,Go应用场景还更侧重后端,框架需求没那么强烈。
Go语言目前应用场景以后端服务居多,对 ORM需求没有网页类应用那么强烈。
一些轻量级的ORM框架已基本满足需求,没有出现推动全自动ORM成熟的力量。
结论
根据上期文章社区朋友的评论和我收集到的一些回答,我觉得全自动ORM框架还是有一定的必要的,所以这次我又更新了一些新的功能,希望对有需要的朋友带来一些帮助。
快速上手
现有一张 Users
表,其表结构如下:
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`age` bigint DEFAULT NULL,
`phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`score` bigint DEFAULT NULL,
`dept` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`created_at` datetime(3) DEFAULT NULL,
`updated_at` datetime(3) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=407 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
对应的数据如下:
INSERT INTO `users` (`username`, `password`, `address`, `age`, `phone`, `score`, `dept`, `created_at`, `updated_at`)
VALUES
('张三', 'password1', '地址1', 25, '12345678901', 80, '部门1', NOW(), NOW()),
('李四', 'password2', '地址2', 30, '12345678902', 90, '部门2', NOW(), NOW()),
('王五', 'password3', '地址3', 35, '12345678903', 70, '部门1', NOW(), NOW()),
('赵六', 'password4', '地址4', 28, '12345678904', 85, '部门2', NOW(), NOW()),
('钱七', 'password5', '地址5', 32, '12345678905', 75, '部门1', NOW(), NOW());
开始使用
下载Gorm-Plus
go get github.com/acmestack/gorm-plus
package main
import (
"github.com/acmestack/gorm-plus/gplus"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"time"
)
type User struct {
ID int64
Username string
Password string
Address string
Age int
Phone string
Score int
Dept string
CreatedAt time.Time
UpdatedAt time.Time
}
var gormDb *gorm.DB
func init() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test?&parseTime=True&loc=Local"
var err error
gormDb, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Println(err)
}
// 初始化gplus
gplus.Init(gormDb)
}
func main() {
users, resultDb := gplus.SelectList[User](nil)
log.Println("error:", resultDb.Error)
log.Println("RowsAffected:", resultDb.RowsAffected)
for _, user := range users {
log.Println("user:", user)
}
}
控制台输出:
2023/06/01 17:48:19 error:
2023/06/01 17:48:19 RowsAffected: 5
2023/06/01 17:48:19 user: &{1 张三 password1 地址1 25 12345678901 80 部门1 2023-06-01 17:48:11 +0800 CST 2023-06-01 17:48:11 +0800 CST}
2023/06/01 17:48:19 user: &{2 李四 password2 地址2 30 12345678902 90 部门2 2023-06-01 17:48:11 +0800 CST 2023-06-01 17:48:11 +0800 CST}
2023/06/01 17:48:19 user: &{3 王五 password3 地址3 35 12345678903 70 部门1 2023-06-01 17:48:11 +0800 CST 2023-06-01 17:48:11 +0800 CST}
2023/06/01 17:48:19 user: &{4 赵六 password4 地址4 28 12345678904 85 部门2 2023-06-01 17:48:11 +0800 CST 2023-06-01 17:48:11 +0800 CST}
2023/06/01 17:48:19 user: &{5 钱七 password5 地址5 32 12345678905 75 部门1 2023-06-01 17:48:11 +0800 CST 2023-06-01 17:48:11 +0800 CST}
搜索工具
只需要下面一行代码即可完成单表的所有查询功能
gplus.SelectList(gplus.BuildQuery[User](queryParams))
例子:
func main() {
http.HandleFunc("/", handleRequest)
http.ListenAndServe(":8080", nil)
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
queryParams := r.URL.Query()
list, _ := gplus.SelectList(gplus.BuildQuery[User](queryParams))
marshal, _ := json.Marshal(list)
w.Write(marshal)
}
假设我们要查询username为zhangsan的用户
http://localhost:8080?q=username=zhangsan
假设我们要查询username姓zhang的用户
http://localhost:8080?q=username~>=zhang
假设我们要查询age大于20的用户
http://localhost:8080?q=age>20
假设我们要查询username等于zhagnsan,password等于123456的用户
http://localhost:8080?q=username=zhangsan&q=password=123456
假设我们要查询username等于zhagnsan,password等于123456的用户
http://localhost:8080?q=username=zhangsan&q=password=123456
假设我们要查询username等于zhagnsan,或者usename等于lisi的用户
可以增加一个分组和gcond的条件查询来实现
http://localhost:8080?q=A.username=zhangsan&q=B.username=lisi&gcond=A|B
所有的单表查询我们都只需要一行代码即可。
更多操作用法请查看:
github.com/acmestack/g…
如果你感兴趣的话,也不妨给个star。