这是我参与「第三届青训营 -后端场」笔记创作活动的第三篇笔记
理解database/sql
database/sql的基本用法
impart (
"database/sql"
"github.con/go-sql-driver/mysql"//import driver
)
func main() {
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")//driver+DSN初始化db连接
rows, err := db.Query("select id, name from users where id = ?", 1)//执行
if err != nil {
// xxx
}
defer ronws.Close()//释放连接
设计原理
GORM使用简介
基本用法
db, err := gorm.Open(mysql.Open("user:password@tcp(127.0.0.1:3306)/hello"))
var users []User
db.Create()//增
err = db.Select("id","name").Find(&users,1).Error //查
db.Model()//更新
db.delete()//删
Model 定义
type Model struct {
ID uint `gorm:"primaryKey`
CreatedAt time.time
UpadateAt time.time
Delete gorm.DeleteAt `gorm:"index"`
}
惯例约定
- 表名为struct name的snake_cases复数格式
- 字段名为field name的snake_case单数格式
- ID/id字段为主键,如果为数字,则为自增主键
- CreatedAt字段,创建时,保存当前时间
- UpdatedAt字段,创建、更新时,保存当前时间
- gorm.DeletedAt字段,默认开启soft delete模式
关联介绍
- CRUD
langAssociation := db.Model(&user).Association("Languages")
langAssociation.Find(&languages)//查
langAssociation.Append()//增
langAssociation.Replace()//改
langAssociation.Deleter()//删
- Preload
db.Preload()//预加载
db.Joins()//sql加载
- 级联删除
//方法1:使用数据库约束自动删除
type User struct {
ID uint
Name string
Account Account `garm: "constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Orders []Order `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
// 需要使用GORM Migrate 数据库前一数据库外键才行
db.AutoMigrate(&User{})
//如果未启用软删除,在删除User时会自动删除其依赖
db.Delete(&User[])
//方法2:使用Select 实现级联删除,不依赖数据库约束及软删除
//删除user时,也删除user的Account记录
db.Select("Account" ).Delete(&user)
//删除user 时,也删除用户及其依赖的所有has one/many、 many2many 记录
db.Select(clause.Associations).Delete(&user)
GORM设计原理
使用GORM的核心思路梳理
一个对象 = 一行数据
示例中的一个User对象,完整地对应到具体users表中的一行数据,让整个框架更加清晰明了。每当数据库增加了一列,就对应地在结构体中加一个字段。这里有两个注意点:
- 不要在核心结构体
User中加入非表中的数据,如一些计算的中间值,引起二义性; - gorm.Model可以提升编码效率(会减少重复编码),但会限制数据库表中字段的定义,慎用(个人更希望它能开放成一个接口);
选择生效字段 = 核心结构体 + 字段数组
在 查询 和 更新 接口里,我推荐的使用方法是采用核心结构体User+一个fields的数组,前者保存具体的数据、也实现了结构体复用,后者则选择生效的字段。
缩短链式调用
GORM的主要风格是链式调用,类似于Builder设计模式、串联堆起一个SQL语句。这种调用方式扩展性很强,但会带来了一个很严重的问题:容易写出一个超长的链式调用,可维护成本大幅度提高。
所以,在推荐使用方式里,区分了两种场景:
- 简单场景 - 核心结构体 + 字段数组
- 复杂场景 - 原生SQL
聚焦微服务的场景
作为一个ORM工具,GORM要考虑兼容各种SQL语句,内部非常庞大的。但如今更多地是考虑微服务的场景,这就能抛开大量的历史包袱,实现得更加简洁。这里我简单列举三个不太推荐使用的SQL特性:
- 减少group by - 考虑将聚合字段再单独放在一个表中
- 抛弃join - 多表关联采用多次查询(先查A表,然后用In语句去B表查)、或做一定的字段冗余(即同时放在A、B两个表里)
- 抛弃子查询,将相关逻辑放在代码里