这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天
Day12
Database/sql 及 GORM 相关解读
课程介绍
本节课将以 Database/sql 的基础概念、设计原理与基本用法切入,进而引出有关GORM 相关解读的相关解读。
理解database/sql
基本用法
// import driver 实现
import (
" database/sql"
_ "github.com/go-sql-driver/mysql"
)
// 使用driver + DSN初始化DB连接
// DSN:Data Source Name,数据库链接字符串
db, err = sql.open("mysql","user:passwordetcp(127.0.0.1:3306)/hello")
// 执行一条SQL,通过rows取回返回的数据
rows, err := db.Query("select id,name from users where id = ?", 1)
// 处理数据后,需要释放链接
// defer rows.Close()
defer func(){
err = rows.Close()
}
// 开始处理数据
var users []User
for row.Next(){
var user User
err := rows.Scan(&user.Id, &user.Name)
users = append(users, user)
}
if rows.Err() != nil {
// ...
}
设计原理
应用程序 -----操作-----> database/sql -----连接&操作-----> 数据库
- DB连接类型
- 直接连接 / Conn
- 预编译 / Stmt
- 事务 / Tx
- 返回数据的处理方式
- Exec / ExecContext -> Result
- Query / QueryContext -> Rows (Columns)
- QueryRow / QueryRowContext-> Row (Rows简化)
GORM基础使用
背景知识
- 关联∶一对一、一对多、单表自关联、多态;Preload、Joins预加载、级联删除;关联模式;自定义关联表
- 事务:事务代码块、嵌套事务、Save Point
- 多数据库、读写分离、命名参数、Map、子查询、分组条件、代码共享、SQL表达式(查询、创建、更新)、自动选字段、查询优化器
- 字段权限、软删除、批量数据处理、Prepared Stmt、自定义类型、命名策略、虚拟字段、自动 track时间、SQL Builder、Logger
- 代码生成、复合主键、Constraint、Prometheus、Auto Miaration、真 · 跨数据库兼容……
- 多模式灵活自由扩展
- Developer Friendly
基本用法
// 使用gorm实现前面的功能
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
func main() {
db, err := gorm.Open(mysql.open("user:password@tcp(127.0.0.1:3306)/hello"))
var users []User
err = db.Select("id", "name").Find(&users, 1).Error
// 操作数据库
db.AutoMigrate(&Product{})
db.Migrator().CreateTable(&Product{})
// [批量]创建
db.Create(&user)
db.Create(&users)
db.CreateInBatches(users, 100)
// 读取
db.First(&user, 1)
db.First(&user, "id = ?", 1)
db.Find(&users, []int{1,2,3})
erros.Is(result.Error, gorm.ErrRecordNotFound) // First Last Take
// 更新
db.Model(&user).Update("name", "李四")
db.Model(&user).UpdateColumn("name", "李四")
db.Model(&user).Updates(User{Name: "李四", Age: 0})
// 注意默认值的区别
db.Model(&user).Updates(map[string]interface{}{"Name": "李四", "Age": 0})
// 批量更新
db.Model(&User{}).Where("age < ?", 18).Updates(map[string]interface{}{"Name": "未成年"})
// 删除
db.Delete(&user)
}
模型定义
// gorm.io/gorm
type Model struct {
ID uint `gorm:"primaryKey"`
CreateAt time.Time
UpdateAt time.Time
DeleteAt gorm.DeleteAt `gorm:"index"`
}
type User struct {
gorm.Model
ID uint // 覆盖?
Name string
Email *string
Age uint8
MemberNumber sql.NullString
ActivateAt sql.NullTime
}
约定优于配置 表名为struct name的snake_cases复数格式
字段名为field name的snake_case单数格式
ID / ld字段为主键,如果为数字,则为自增主键
CreatedAt字段,创建时,保存当前时问 6510
UpdatedAt字段,创建、更新时,保存当前时间gorm.
DeletedAt字段,默认开启soft delete模式 一切都可配置
关联
/*
User 拥有一个 Account (has one),拥有多个 Pets (has many),多个 Toys (多态has many),
属于某Company (belongs to),属于某Manager (单表 belongs to),管理Team(单表 has many),
会多种Languages (many to many),拥有很多 Friends(单表 many to many),
并且他的 Pet也有一个玩具Toy(多态has one)
*/
type User struct {
gorm.Model
Name string // go 基本类型
Account Account
Pets []*Pet
Toys []Toy `gorm:"polymorphic:Owner`
CompanyID *int
Company Company
ManagerID *uint
Manager *User
Team []User `gorm: "foreignkey:ManagerID"`
Languages []Language `gorm:"many2many:UserSpeak;"`
Friends []*User `gorm : "many2many:user_friends;`
// CRUD
db.Save(&User{})
langAssoc := db.Model(&user).Association("Languages")
langAssco
.Find()
.Append()
.Replace()
.Delete()
.Clear()
.Count()
// Preload / Joins 预加载
// 等于多条单表sql, 能查关联表的多条数据
db.Preload("Orders").Preload("Profile").Find(&users)
// 等于单条单表sql,只查关联表的一条数据
db.Joins.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users)
// 多级预加载
db.Preload("Orders.OrderItems.Product").Find(&users)
// 级联删除
db.Select("Orders", "Orders.BillingAddress", "CreditCards").Delete(&user)
}