Database/sql 与 GORM基础 | 青训营笔记

87 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天

Day12

Database/sql 及 GORM 相关解读

视频

PPT

课程介绍

本节课将以 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)

}