GORM的特性
- 全功能 ORM
- 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持
Preload、Joins的预加载 - 事务,嵌套事务,Save Point,Rollback To Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
连接到数据库
GORM 官方支持的数据库类型有:MySQL, PostgreSQL, SQLite, SQL Server 和 TiDB
对于mysql
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
可以用以上代码来实现GORM连接到mysql数据库。
使用GORM的核心思路梳理
一个对象 = 一行数据:例如一个User对象,完整地对应到具体users表中的一行数据,每当数据库增加了一列,就对应地在结构体中加一个字段,但要注意:1.不要在核心结构体例如User中加入非表中的数据,如一些计算的中间值,引起二义性;2.gorm.Model可以提升编码效率(会减少重复编码),但会限制数据库表中字段的定义。
两个核心文件
在GORM库中,有两个核心的文件,也是我们调用频率最高的函数所在:chainable_api.go和 finisher_api.go。顾名思义,前者是整个链式调用的中间部分,后者则是最终获取结果的函数。以查询为例:
db.Where(\&User{Name: "jinzhu"}, "name", "Age").Find(\&users)
其中Where是chainable,也就是还在拼接SQL条件,Find则是触发真正查询的finisher。
GROM的四种重要观点
-
Builder设计模式 - 在面对复杂场景中,Builder设计模式扩展性很好,可分为两个阶段:存储数据+处理数据;GORM的调用就是采用了chainable+finisher的两段实现,前者保存SQL相关元数据,后者拼接SQL并执行;
-
负重前行 - GORM是一个负重前行的框架:它不仅支持了所有原生SQL的特性,也增加了很多类似Hook的高级特性,导致这个框架非常庞大。如果团队没有历史包袱,更推荐节制地使用GORM特性,适当封装一层;
-
interface{}问题 - GORM中许多函数入参的数据类型都是interface{},底层又用reflect支持了多种类型,这种实现会导致两个问题:
-
reflect导致的底层的性能不高(这点还能接受)
-
interface{}如果传入了不支持的复杂数据类型时,排查问题麻烦,往往要运行程序时才会报错。
- 高频拼接重复SQL - 在一个程序运行过程中,执行的SQL语句都比较固定,而变化的往往是参数;从GORM的实现来看,每次执行都需要重新拼接一次SQL语句,是有不小的优化空间的,比如引入一定的cache。