项目简介
GORM 是一款 设计简洁、功能强大、自由扩展的全功能 ORM ,遵循了 API 精简、测试优先、最小惊讶、灵活扩展、无依赖 可信赖的设计原则。GORM做为一个全功能的ORM,提供了完善的功能,例如:
关联:一对一、一对多、单表自关联、多态关联;Preload、Joins 预加载;关联模式
事务:嵌套事务, Save Point
Hooks、Callbacks 自由扩展
多数据库、读写分离、Prometheus、Prepared Stmt、查询优化器、批量数据处理、代码共享、子查询、DryRun
SQL Builder、Smart Migration、复合主键、自定义类型 (JSON等)、SQL 表达式查询创建更新、虚拟字段…
真 • 跨数据库兼容等功能
开源历程
GORM 第一次大概是 2013 年开源,当时在用 Go 写一套支付系统,当时 Go 的生态圈也不太成熟,总感觉别人家的轮子的不够圆,所以准备自己造个更圆的轮子,于是就有了GORM。GORM 在开源过程中收益众多,收集了很多反馈让我们向着更好不断迭代,还收集了特别多的边缘情况,也让我读了各数据库的相关文档好多遍,极大的帮我们避免了不少的潜在问题,让我们的系统稳定性提高了很多,所以开源给我们的收益还是挺大的。
GORM V2.0 的开发起因于 2020 年春节疫情假期刚好空出些时间,然后开始了 2.0 版本的开发计划。2.0 版本是吸取了 1.0 版本的一些经验教训,从零开始完全重写的版本,在重写的过程中性能、灵活性、扩展性都得到了很大的提升。
分享提要
本次分享主要从 GORM 的 CRUD 基本操作入手,然后延伸讲解了一些高阶的使用方法,例如使用 Map 更新创建查询、批量插入/更新、SQL 表达式、默认值、零值问题、关联操作、SQL Builder、命名参数等等。
项目仓库:https://github.com/go-gorm/gorm
项目文档:https://gorm.cn
演讲视频:
https://www.bilibili.com/video/BV1ST4y1T7NR
演讲PPT:
https://www.slideshare.net/JinzhuZhang2/gorm-241179148
补充介绍
本次分享因为时间所限主要讲解了普通用户最常用的一些 CRUD 的功能介绍,还有挺多 GORM 的特性没来得及和大家探索,不过 GORM 的文档提供了比较全面的介绍,欢迎大家参考文档了解详情,这里给大家罗列了一个列表参考。
做为普通用户除了上面的介绍你还必须要知道的:
Method Chain 的线程安全
https://gorm.cn/zh_CN/docs/method_chaining.html
防止 SQL 注入
https://gorm.cn/zh_CN/docs/security.html
错误处理
https://gorm.cn/zh_CN/docs/error_handling.html
做为普通用户你最好知道的:
GORM 优于配置的一些约定
https://gorm.cn/zh_CN/docs/models.html#Conventions
如何给字段来配置读写权限
https://gorm.cn/zh_CN/docs/models.html#field_permission
给多字段配置时间追踪
https://gorm.cn/zh_CN/docs/models.html#time_tracking
自关联定义 (一对一,多对多,一对多,单表自关联,多态)、自定义 foreign key, reference、复合外键、自定义 JoinTable
Query 的一些 API: Pluck, FirstOrInit, FirstOrCreate (Assign, Attrs)
https://gorm.cn/zh_CN/docs/advanced_query.html#FirstOrInit
Migrations
https://gorm.cn/zh_CN/docs/migration.html
字段的 Tags 特殊配置支持
https://gorm.cn/zh_CN/docs/models.html#tags
如何使用 Context
https://gorm.cn/zh_CN/docs/context.html
Transactions, Nested Transactions, Save Point, RollbackTo to Saved Point
https://gorm.cn/zh_CN/docs/transactions.html
Gorm Config (跳过默认事务、修改命名策略、修改当前时间函数、只生成 SQL 不执行模式、Prepared Stmt 加速模式等等)
https://gorm.cn/zh_CN/docs/gorm_config.html
Session 模式概念及其配置 (如:跳过默认事务、修改命名策略、修改当前时间函数、只生成 SQL 不执行模式、Prepared Stmt 加速模式等等)
https://gorm.cn/zh_CN/docs/session.html
定义索引、复合索引、优先索引、约束等
https://gorm.cn/zh_CN/docs/indexes.html https://gorm.cn/zh_CN/docs/constraints.html
数据库连接池配置
https://gorm.cn/zh_CN/docs/generic_interface.html
数据库的不同连接参数,以 mysql 为例子
https://github.com/go-gorm/mysql#gorm-mysql-driver
Hooks 介绍
https://gorm.cn/zh_CN/docs/hooks.html
做为资深用户你还需要知道:
读写分离 / 多数据库
https://gorm.cn/zh_CN/docs/dbresolver.html
使用查询优化器,指定索引查询
https://gorm.cn/zh_CN/docs/hints.html
Prometheus 集成
https://gorm.cn/zh_CN/docs/prometheus.html
使用复合主键
https://gorm.cn/zh_CN/docs/composite_primary_key.html
分库分表 / 代码共享
https://gorm.cn/zh_CN/docs/scopes.html
Embedded Struct 定义共享 struct (参考 embedded tag)
自定义数据类型(json 等数据类型支持)
https://gorm.cn/zh_CN/docs/data_types.html
如何提升性能
https://gorm.cn/zh_CN/docs/performance.html
如果你是一个定制开发者你需要知道的:
多看源码,贡献社区 ????
https://github.com/go-gorm/gorm
熟悉 Statement & Clause 概念
https://www2.slideshare.net/JinzhuZhang2/gorm-gopher-china
定制 Callbacks 插件
https://gorm.cn/zh_CN/docs/write_plugins.html
了解如何定制 driver 实现特殊需求 (各 driver 源码,
https://gorm.cn/zh_CN/docs/write_driver.html)
如何定制 logger
https://gorm.cn/zh_CN/docs/logger.html
Set/Get/InstanceSet/InstanceGet Callback 传递参数
https://gorm.cn/zh_CN/docs/settings.html
问答环节
1.如何设置表名前缀
可以通过配置 gorm.Config 的 NamingStrategy 实现需求,具体参考:
https://gorm.io/docs/gorm_config.html#NamingStrategy
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_", // NOTE 这里
SingularTable: true,
SingularTable: true,
},
})
默认的 NamingStrategy 可以实现一些常见的配置,如果不能满足需求的话,可以选择自定义 Namer 的 interface,例如:
type Namer interface {
TableName(table string) string
ColumnName(table, column string) string
JoinTableName(table string) string
RelationshipFKName(Relationship) string
CheckerName(table, column string) string
IndexName(table, column string) string
}
2.db Session 是什么?他和 db connection 有什么关系?
DB Session 只是一个变量,和 db Connection 无关。对于一个新的 DB Session 来说,还会共享原来的连接池。他的作用主要在于可用来给每个 Session 做一些常见的配置,常见配置例如跳过默认事务、修改命名策略、修改当前时间函数、只生成 SQL 不执行模式、Prepared Stmt 加速模式等,详情请参考
https://gorm.io/docs/session.html
还有一个要注意的问题是 Method Chain 的线程安全问题,可参考
https://gorm.io/docs/method_chaining.html
3.clause.Associations 进行级连删除时发生错误会怎么样?
GORM 默认的所有的创建/更新/删除都在一个事务内,如果在当中发生错误的话,会进行整体回滚。
另外关于级连删除,他只会删除依赖他的关联 (has one, has many, many2many 中间表),而不会删除他依赖的关联 (belongs to, many2many 源数据),举个例子,如果有一个用户,他在一个公司工作,他还拥有一个宠物,在对他进行级联删除的时候,只会删除这个用户,以及他所拥有的宠物,而不会删除这个人的公司。
4.如何在测试中 mock 数据
可以使用 https://github.com/DATA-DOG/go-sqlmock 先建立一个 connection,例如
import "github.com/DATA-DOG/go-sqlmock"
mockdb, mock, err := sqlmock.New()
然后在测试时可以通过使用现有连接的方式来配置 GORM,如果使用 mysql 数据库,可以指定 mysql 的 driver,其它数据库需要使用相应的 driver:
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
gormDB, err := gorm.Open(mysql.New(mysql.Config{
Conn: mockdb,
}), &gorm.Config{})
不过个人还是推荐能使用真实数据库测试就使用真实数据库进行测试,毕竟行为还是有些不一样,使用真实数据库可以更好的、更早的发现线上问题。
5.可以 Preload 预加载的时候使用其它的数据库么?例如 postgres, mongodb
默认不支持从其它的数据库预加载,不过支持从同一种数据库的其它服务 IP 上加载,这部分可以参考 db resolver,多数据库模式 https://gorm.io/docs/dbresolver.html
如果想支持其它类的数据库或从缓存服务器上查询数据的话,需要通过自定义 callbacks,然后在 callbacks 里根据从 db.Statement
获取当前的条件,然后根据这些条件从相应的其它的数据库、缓存数据库中查询出相应数据并赋值回原对象中。