GORM特性 | 青训营笔记

134 阅读2分钟

GORM的特性

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,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的四种重要观点

  1. Builder设计模式 - 在面对复杂场景中,Builder设计模式扩展性很好,可分为两个阶段:存储数据+处理数据;GORM的调用就是采用了chainable+finisher的两段实现,前者保存SQL相关元数据,后者拼接SQL并执行;

  2. 负重前行 - GORM是一个负重前行的框架:它不仅支持了所有原生SQL的特性,也增加了很多类似Hook的高级特性,导致这个框架非常庞大。如果团队没有历史包袱,更推荐节制地使用GORM特性,适当封装一层;

  3. interface{}问题 - GORM中许多函数入参的数据类型都是interface{},底层又用reflect支持了多种类型,这种实现会导致两个问题:

  • reflect导致的底层的性能不高(这点还能接受)

  • interface{}如果传入了不支持的复杂数据类型时,排查问题麻烦,往往要运行程序时才会报错。

  1. 高频拼接重复SQL - 在一个程序运行过程中,执行的SQL语句都比较固定,而变化的往往是参数;从GORM的实现来看,每次执行都需要重新拼接一次SQL语句,是有不小的优化空间的,比如引入一定的cache。