搞懂常见Go ORM系列-开篇

一、引言

简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。

在Go中,ORM可以把数据库里的表格映射成 Go 语言中的结构体,然后你只需要像操作对象那样去增删改查数据,而不必烦恼那些冗长的 SQL 语句,同时很多 ORM 框架都支持多种数据库,切换底层数据库时只需要做简单配置

但受限于Go本身的语法,目前主流的ORM,大部分都非常的难用

萝卜青菜各有所爱,本系列为大家介绍一些主流的Go ORM,供大家对比选择

二、常见ORM

在v站的一篇帖子下,我收集了回帖中提到比较多的ORM,并根据内部实现机制和设计理念,将Go ORM 框架分成了三类

🌲 反射型 主要通过反射机制将结构体映射到数据库表上,代表作为 go-gorm/gorm

🌲 代码生成型 通过代码生成工具预先生成数据模型及查询构建器,代表作有 ent/ent 和日益流行的 go-gorm/gen

🌲 SQL 增强型 基于原生 SQL 库进行封装和扩展,既保留 SQL 的灵活性,又提供了一系列便捷函数,代表作为 jmoiron/sqlx

实现方式 Go ORM库 star
reflect to struct go-gorm/gorm 36.6k
reflect to struct go-xorm/xorm 6.7k
reflect to struct upper/db 3.5k
Code gen go-gorm/gen 2.3k
Code gen ent/ent 15.9k
Code gen sqlc-dev/sqlc 14.2k
SQL enhance Masterminds/squirrel 7.1k
SQL enhance jmoiron/sqlx 16.6k

1、反射型

反射型 ORM 利用 Go 语言的反射机制,在运行时将结构体的字段和标签动态映射到数据库表中,自动生成 SQL 语句来实现 CRUD 操作。这种方式无需预先生成代码,使用较为简单直观

优势

  • 开发简便:直接基于结构体定义进行数据库操作,无需额外生成代码
  • 动态灵活:自动识别结构体字段和标签,支持自动迁移和关联查询

缺点

  • 性能开销:反射机制会引入运行时开销,在高并发场景下可能成为瓶颈
  • 类型安全不足:导致部分类型错误依赖运行时检查,无法在编译期捕获

go-gorm/gorm 是目前最流行的反射型 ORM 框架,甚至可以说是Go中最流行的ORM 框架

个人感受,受限Go语法加上作者的设计思路,导致gorm过度依赖 interface{},各种隐式规则,使用时相当不方便,有一种让Go退化成了弱类型的感觉,不仅没有办法做到字段提示,每次传参都得看文档

其示例代码如下:

go 复制代码
package main

import (
	"log"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name  string `gorm:"not null"`
	Email string `gorm:"unique;not null"`
}

func main() {
	db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}

	// 创建记录
	db.Create(&User{Name: "Alice", Email: "[email protected]"})

	// 查询记录
	var user User
	db.First(&user, "name = ?", "Alice")
	log.Printf("User: %+v\n", user)
}

2、代码生成型

因为Go语法限制,反射实现注定没有办法做到类型安全,因此催生出了代码生成这个流派

代码生成型 ORM 框架通过解析开发者定义的 schema,然后生成类型安全的代码文件,这种方式能在编译时捕获类型错误,同时避免运行时反射带来的性能损耗

虽然使用前需要执行代码生成步骤,但在大型项目中,这种方式能提供更高的安全性和性能

优势

  • 编译期类型检查:生成的代码可以在编译时捕获错误
  • 高运行时性能:无需依赖反射,运行时效率更高

缺点

  • 额外生成步骤:需要预先运行代码生成命令,增加构建流程
  • 开发流程复杂:生成代码与手写代码之间的同步和维护可能增加开发难度,尤其在频繁修改 schema 时

go-gorm/gen 是与 GORM 配套的代码生成工具,是代码生成型ORM的后起之秀,为 GORM 提供了更强的类型安全查询接口,提升了性能和编译时检查能力

ent/ent 是目前代码生成型ORM中最流行的,个人相对比较喜欢,虽然跟Rust或者TS的ORM相比体验还是差很多, 哎,没办法啊

Ent需要开发者先定义 schema,然后通过命令行工具生成代码。生成的代码包含了模型、查询构建器和数据库迁移工具,从而确保数据库操作的类型安全和高效性

以下示例忽略了schema和代码生成部分

go 复制代码
package main

import (
    "context"
    "log"
    "entgo.io/ent/dialect/sql"
    _ "github.com/mattn/go-sqlite3"
    "entgo.io/ent/examples/start/ent"
)

func main() {
    client, err := ent.Open(sqlite.Open("./ent.db"), &sql.Config{Driver: "sqlite3"})
    if err != nil {
        log.Fatal("failed to connect to the database:", err)
    }
    defer client.Close()

    // 创建一个新用户
    user, err := client.User.Create().
        SetName("Alice").
        SetEmail("[email protected]").
        Save(context.Background())
    if err != nil {
        log.Fatal(err)
    }
    log.Println("User created:", user)
}

3、SQL增强型库

有一部分人认为ORM是一个鸡肋的概念,尤其在Go中,因此选择裸写SQL的方式

SQL增强型库在原生 SQL 库的基础上进行扩展,不隐藏 SQL 语句,而是通过提供额外的封装函数和数据映射机制,帮助开发者更高效地执行查询和数据转换。它保持了 SQL 的灵活性,同时简化了常见操作。

优势

  • 灵活性高:保留 SQL 语句的编写自由,适合复杂查询
  • 数据映射简化:自动绑定查询结果到结构体,减少手动转换代码
  • 轻量高效:直接使用原生 SQL,无额外 ORM 开销

缺点

  • 需要手写 SQL:开发者必须自行编写 SQL,可能会增加样板代码和维护难度
  • 缺少高级抽象:没有 ORM 提供的自动关联、迁移和数据验证功能

jmoiron/sqlx 是一款流行的 SQL 增强型库,其示例代码如下:

go 复制代码
package main

import (
	"log"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"
)

type User struct {
	ID    int    `db:"id"`
	Name  string `db:"name"`
	Email string `db:"email"`
}

func main() {
	db, err := sqlx.Connect("sqlite3", "sqlx.db")
	if err != nil {
		log.Fatal(err)
	}

	// 查询数据
	var user User
	err = db.Get(&user, "SELECT * FROM users WHERE id=?", id)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("User: %+v\n", user)
}

三、总结

总结下:Go ORM 框架各有优势与不足

  • 反射型(如 GORM)开发简单、上手快,但反射带来的性能开销和语法缺陷,导致用起来很别扭,但好在GORM应用广泛,网上的资料和文档也比较多
  • 代码生成型(如 Ent、go-gorm/gen)通过生成类型安全的代码提供了更高的性能和编译时检查,但需要额外的代码生成步骤,开发流程相对复杂
  • SQL增强型库(如 sqlx)在保留 SQL 灵活性的同时,通过扩展封装提升了开发效率,非常适合不喜欢ORM概念的开发者

总之,受限Go语法目前没有特别好用的ORM库,大家只能根据个人喜欢和流行程度来自行选择了

后续会为大家带来每个ORM库的详细用法的介绍


✨ 微信公众号【凉凉的知识库】同步更新,欢迎关注获取最新最有用的知识 ✨

相关推荐
bobz9652 分钟前
ovs patch port 对比 veth pair
后端
Asthenia041212 分钟前
Java受检异常与非受检异常分析
后端
uhakadotcom26 分钟前
快速开始使用 n8n
后端·面试·github
JavaGuide33 分钟前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz96543 分钟前
qemu 网络使用基础
后端
Asthenia04121 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04121 小时前
Spring 启动流程:比喻表达
后端
Asthenia04122 小时前
Spring 启动流程分析-含时序图
后端
ONE_Gua2 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
致心2 小时前
记一次debian安装mariadb(带有迁移数据)
后端