设计模式之 Database/SQL 与 GORM 实践 | 青训营笔记
这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记
概述
本课程目标为带领同学们了解 Go 的 Database/SQL的实现,了解 GORM 的实现原理、使用简单,以及如何基于 GORM 做一些定制化开发。
课程结构为:
- 理解 database/sql
- GORM 使用简介
- GORM 设计原理
- GORM 最佳实践
主要内容
01. 理解 Database/SQL
1.1 Database/SQL 的基本用法
package quick_start
import (
"database/sql"
)
type User struct {
ID int
Name string
}
func main() {
//使用driver + DSN 初始化DB连接
db, err := sql.Open("mysql", "zzb:123456@tcp(127.0.0.1:3306)/hello ")
//执行一条sql.通过rows取回返回的数据处理完毕,需要释放连接
rows, err := db.Query("select id ,name from users where id = ?", 1)
if err != nil {
//XXX
}
//数据错误处理
defer rows.Close() //可能会返回一个错误,需要进行检查
defer func() {
err = rows.Close()
}()
var users []User
for rows.Next() {
var user User
rows.Scan(&user.ID, &user.Name)
if err != nil {
}
users = append(users, user)
//处理错误
if rows.Err() != nil {
//....
}
}
}
1.2 设计原理

1.2.1 连接池配置
func(db *DB) SetConnMaxIdleTime(d time.Duration)
func(db *DB) SetConnMaxLifeTime(d time.Duration)
func(db *DB) SetConnMaxIdleConns(n int)
func(db *DB) SetConnMaxOpenConns(n int)
1.2.2 连接池状态
func (db *DB) Stats() DBStats
1.2.3 操作过程伪实现
func main() {
for i := 0; i < maxBadConnRetries; i++ {
//从连接池获取连接或通过 driver 新建连接
dc, err := db.conn(ctx, strategy)
//有空闲连接 -> reuse -> max life time
//新建连接 -> max open...
//将连接放回连接池
defer dc.db.putConn(dc, err, true)
//validateConnection 有无错误
//max life time,max idle conns 检查
//连接实现 friver.Queryer , driver,Execer 等 interface
if err == nil {
err = dc.ci.Query(sql, args...)
}
isBadConn = errors.Is(err, driver.ErrBadConn)
if !isBadConn {
break
}
}
}
1.2.4 Driver连接接口实现1
func main(){
db, err := sql.Open("mysql", "zzb:123456@tcp(127.0.0.1:3306)/hello ")
}
//注册Driver
func init(){
sql.Register("mysql".&MySQLDriver{})
}
1.2.5 Driver连接接口实现2
func main() {
connector, err := mysql.NewConnector(&mysql.Config{
User: "gorm",
Passwd: "gorm",
Net: "tcp",
Addr: "127.0.0.1:3306",
DBName: "gorm",
ParseTime: true,
})
db := sql.OpenDB(connector)
}
1.2.6 操作接口

02. GORM 的使用简介
2.1 背景知识
设计简洁、功能强大、只有扩展的全功能ORM
-
设计原则: API 精简、测试优先、最小惊讶、灵活扩展、无依赖可信赖
-
功能完善:
- 关联:一对一、一对多、单表自关联、多态;Preload、 Joins 预加载、级联删除;关联模式;自定义关联表
- 事务:事务代码块、嵌套事务、Save Point
- 多数据库、读写分离、命名参数、Map、 子查询、分组条件、代码共享、SQL 表达式(查询、创建、更新)、自动选字段、查询优化器
- 字段权限、软删除、批量数据处理、Prepared Stmt、自定义类型、命名策略、虚拟字段、自动track时间、SQL Builder、Logger
- 代码生成、复合主键、Constraint、 Prometheus、 Auto Migration、真·跨数据库兼容…
- 多模式灵活自由扩展
- Developer Friendly
2.2 GORM 的基本用法
详见GROM文档gorm.io/docs/index.…
2.3 Model 定义
-
惯例约定
- 约定优于配置,配置也可以修改
- 表名为
struct name的snake_ cases复数格式 - 字段名为
field name的snake_ case单数格式 ID/ ld字段为主键,如果为数字,则为自增主键CreatedAt字段,创建时,保存当前时间UpdatedAt字段,创建、更新时,保存当前时间gorm.DeletedAt字段,默认开启soft delete模式
2.4 关联介绍
- Belongs To 属于
- Has Many 一对多
- Has One
- Many To Many 多对多
- 预加载
- 级联删除
03. GORM 的设计原理
3.1 SQL 生成的机制
-
自定义 Clause Builder
- 支持不同版本的SQL
-
方便扩展 Clause
-
自由选择 Caluses
3.2 插件扩展机制
- Fiisher Method -> 决定 Statement类型 -> 执行Callbacks -> 生成 SQL并执行
3.3 ConnPool 扩展机制

- 查找缓存的预编译SQL
- 未找到,将收到的SQL和Vars预编译
- 使用缓存的预编译SQL执行
开关:interpolateParams = false
3.4 Dialector 扩展机制
- 定制SQL生成
- 定制GORM插件
- 定制ConnPool
- 定制企业特定逻辑
04. GORM 最佳实践
4.1 GORM 最佳实践
-
数据序列化与SQL表达式
- SQL表达式查询
- 数据序列化
-
批量数据操作
- 批量创建/查询
- 批量更新
- 批量数据加速操作
-
代码复用、分库分表、sharding
-
混沌工程/压测
-
Logger/Trace
-
Migrator
-
Gen代码生成/Raw SQL
-
安全