设计模式之 Database/SQL 与 GORM 实践
概述
课程ppt bytedance.feishu.cn/file/boxcnc…
-
知道什么是数据库、什么是 SQL
-
知道如何使用 database/sql 建立连接、使用
-
对 GORM 有个简单的认知
-
理解 Database/SQL
-
Database/SQL 的基本用法
-
设计原理
-
- 基础概念
- GORM 的使用简介: 是当今比较热门的 golang 的 orm 操作数据库的工具,使用上主要是把 struct 类和数据库表进行映射,操作数据库时无需手写 sql。本质就是提供一组函数来帮助我们快速拼接 sql 语句,尽量减少 sql 的编写
github 地址:github.com/go-gorm/gor…
取数据逻辑:
- 从数据库读取的数据会先保存到预先定义的模型对象,然后我们就可以从模型对象得到我们想要的数据
存数据逻辑:
- 存数据到数据库也是先新建一个模型对象,然后把数据保存到模型对象,最后把模型对象保存到数据库
全功能 ORM
关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
Create,Save,Update,Delete,Find 中钩子方法
支持 Preload、Joins 的预加载
事务,嵌套事务,Save Point,Rollback To Saved Point
Context、预编译模式、DryRun 模式
批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
复合主键,索引,约束
Auto Migration
自定义 Logger
灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
每个特性都经过了测试的重重考验
开发者友好
先码一下gorm的官方文档:文档地址
- GORM 的基本用法
安装:
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
连接数据库 :
dsn := "username:passwd@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
}
fmt.Println(db)
自动创建表
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
Id int64 `gorm:"primary_key" json:"id"` // 设置id为主键
Username string
Password string
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
}
fmt.Println(db)
db.AutoMigrate(User{}) // 自动创建表结构
Model 定义:
惯例约定:
注:Column 指定列的名称
Type 指定列的类型
Size 指定列的大小,默认是 255
PRIMARY_KEY 指定一个列作为主键
UNIQUE 指定一个唯一的列
DEFAULT 指定一个列的默认值
PRECISION 指定列的数据的精度
NOT NULL 指定列的数据不为空
AUTO_INCREMENT 指定一个列的数据是否自增
INDEX 创建带或不带名称的索引,同名创建复合索引
UNIQUE_INDEX 类似 索引 ,创建一个唯一的索引
EMBEDDED 将 struct 设置为 embedded
EMBEDDED_PREFIX 设置嵌入式结构的前缀名称
-
忽略这些字段
关联介绍:
package models
import (
"github.com/jinzhu/gorm"
"testing"
)
import _ "github.com/jinzhu/gorm/dialects/sqlite"
func TestAsso(t *testing.T) {
db, err := gorm.Open("sqlite3", "./gorm.db")
if err != nil {
t.Error(err)
}
defer db.Close()
db.LogMode(true)
db.AutoMigrate(&User{}, &Address{}, &Email{}, &Language{})
addr := Address{Address1: "My addr"}
db.Save(&addr)
user := User{
Name: "jinzhu",
BillingAddress: addr,
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "jinzhu@example.com"},
{Email: "jinzhu-2@example.com"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}
// 对于主键为空的item,不去创建
//db.Debug().Set("gorm:association_autocreate", false).Create(&user)
// 对于主键非空的item,不去更新,即:只保存外键关联那个字段,不更新关联对象的值
//db.Debug().Set("gorm:association_autoupdate", false).Create(&user)
// 不更新外健
//db.Debug().Set("gorm:association_save_reference", false).Create(&user)
// 默认上述开关全打开
db.Create(&user)
//db.Debug().Set("gorm:association_autocreate", false).
// Set("gorm:association_autoupdate", false).
// Set("gorm:association_save_reference", false).
// Create(&user)
langs := []Language{}
// 查找关联
db.Model(&user).Association("Languages").Find(&langs)
emails := []Email{}
db.Model(&user).Association("Emails").Find(&emails)
// 添加关联
db.Model(&user).Association("Emails").Append(Email{Email: "abc@qq.com"})
// 替换关联, 如果item主键为空,默认会新建
email := emails[0]
db.Model(&user).Association("Emails").Replace([]Email{email, {Email: "def@qq.com"}})
// 删除关联,item主键非空才生效
def := Email{}
db.First(&def, "Email = ?", "def@qq.com")
db.Model(&user).Association("Emails").Delete(def)
// 清空关联,无论是替换,删除,还是清空,只会删除关联的引用,不会删除关联本身
db.Model(&user).Association("Emails").Clear()
print("ok")
}
使用orm框架进行CRUD
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "root:12345678@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//ORM操作
GormCreate(db)
}
func GormCreate(db *gorm.DB) {
//create
student := Student{Name: "sss", Age: 10, Address: "Guangzhou"}
res := db.Create(&student)
fmt.Println(student.Id)
fmt.Println(res.Error)
}
原因:
操作对象,就相当于直接操作数据库表,不需要占位符
代码量少不易出错
级联删除:
- GORM 的设计原理:
SQL 生成的机制:
插件扩展机制
ConnPool 扩展机制
Dialector 扩展机制
- GORM 最佳实践