后端Go语言基础笔记:
这是我参与【第五届青训营】伴学笔记创作活动的第5天。
在第十一节的学习中掌握了:
什么是Gorm:
它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的打交道,只要像平时操作对象一样操作它就可以了 。
gorm特点:
- 全功能ORM;
- 关联(包含一个,包含多个,属于,多对多,多种包含);
- Callbacks(创建/保存/更新/删除/查找之前/之后);
- 预加载;
- 事务
- 复合主键
- SQL Builder
- 自动迁移
- 日志
- 可扩展,编写基于GORM回调的插件
- 每个功能都有测试
- 开发人员友好
gorm插件工作:
//注册新Callback
db.Callback().Create().Register("myplugin", func(*gorm.DB) {})
//删除 Callback
db.Callback().Create().Remove("gorm:begin_transaction")
//替换 Callback
db.Callback().Create().Replace("gorm:before_create",func(*gorm.DB) ,{})
// 查询注册的 Callback
db.Callback().Create().Get("gorm:create")
//指定Callback顺序
db.Callback().(reate().Before("gorm:create").After("myplugin").Register("myplugin2", func(*gorm.DB) ,{})
//注册到所有服务之前
db.Callback().Create().Before("*").Register("my-plugin:new_callback", func(*gorm.DB) {})
//注册时检查条件
enableTransaction := func(db *gorm.DB) bool { return !db.SkipDefaultTransaction}
db.callback().Create().Match(enableTransaction).Reaister("gorm:begin_transaction".BeginTramsaction)
gorm插件工作-多租户
// 根据 TenantID 过滤
func setTenantScope(db *gorm.DB) {
if tenantID,err := getTenantID(db.Statement.Context); err != nil {
db.Where("tenant_id = ?" tenantID)
}else {
db.AddError(err)
}
}
db.Callback.Query().Before("gorm:query").Register("set_tenant_scope",setTenantScope)
db.Callback.Delete().Before("gorm:delete").Register("set_tenant_scope",setTenantScope)
db.Callback.Update().Before("gorm:update").Register("set_tenant_scope",setTenantScope)
// 设置 TenantID
func setTenantID(db *gorm.DB) {
tenantID,err := getTenantID(db.Statement.Context)
db.Statement.SetColumn("tenant_id", tenantID)
//...
}
db.Callback.Create().Before("gorm:create").Register("set_tenant_id",setTenantID)
gorm插件工作-多数据库、读写分离
-
MySQL 主从复制是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。
-
MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,数据的更新可以在远程连接上进行,从节点可以复制主数据库中所有的数据库或者特定的数据库,或者特定的表。
DB.Use(dbresolver.Register(dbresolver.Configf
// db2’作为 sources, db3 、db4 作为 replicas
Sources:gorm.Dialectorf{ mysql.Open("db2_dsn")},
Replicas: []gorm.Dialectorf {mysql.Open("db3_dsn"),mysql.Open("db4_dsn")},
// sources/replicas 负载均衡策略
Policy: dbresolver.RandomPolicy{},
}).Register(dbresolver.Configf
// `db1`作为 sources (DB 的默认连接),对于UserAddress’使用db5作为 replicas
Replicas: gorm.Dialectorf {mysgl.Open("db5_dsn")},
},&User[],&Address{}).Register(dbresolver.Configf
//db6db7、作为 sources,对于 orders 、Product 使用 db8 作为 replicas]
Sources:[]gorm.Dialector{ mysql.Open("db6_dsn"),mysql.Open("db7_dsn")},
Replicas:[]gorm.Dialectorf mysql.Open("db8_dsn")},
}."orders",&Product{},"secondary"))
// 使用 Write 模式:从 sources db db1 读取 user
DB.Clauses(dbresolver.Write).First(&user)
// 指定 Resolver: 从secondary’的 replicas db db8读取 user
DB.Clauses(dbresolver.Use("secondary")).First(&user)
//指定 Resolver 和 Write 模式: 从 secondary 的 sources db db6 或 db7 读取 user
DB.Clauses(dbresolver.Use("secondary"),dbresolver.Write).First(&user)
第十二节:
gorm 新建数据的方法有 Create()方法,但不能实现部分插入。它采用整体插入,struct中声明变量都会转化为 sql 语句中的字段插入,对于未给赋值的变量采用对应类型的零值来插入。
数据序列化方法:
- 当然,也有反序列化的操作
//自定义数据格式实现接口 Scanner,Valuer
func (j JSON) Value() (driver.Value,error) {/** **/}
func (j *JSON) Scan(value interface{}) error {/** **/}
//自定义数据格式实现 Serializer 接口
type Password string
type SerializerInterface interface {}
Scan(context.Context,f *schema.Field,dst reflect.Value,dbValue interface{})error
Value(context.Context,f *schema.Field,dst reflect.Value,fieldV interface{})(interface{},
error)
数据操作-批量查询/创建:
- 有2种方法:一种是通过rows.Scan的方式,一种是通过db.ScanRows的方式
// 批量创建
var users = []User{{Name: "jinzhu1"},{Name: "jinzhu2"},{Name: "jinzhu3"}}
db.Create(&users)
db .CreateInBatches(users,100)
for _, user := range users {
user.ID // 1,2,3
}
//批量查询
rows,err := db.Model(&User{}).Where("role = ?","admin").Rows()
for rows.Next() {
// 方法1: sgl.Rows Scan
rows.Scan(&name,&age,&email) // NULL 值的情况?
// 方法2: gorm ScanRows
db.ScanRows(rows,&user)
//xxx
}
DB .Where("role = ?" ,"admin").FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error{})
数据操作-批量数据加速操作
//方法1:关闭默认事务
db,err := gorm.Open(sqlite.Open("gorm.db"),&gorm.Config{
SkipDefaultTransaction: true
})
db.Create(&user)
tx := db.Session(&SessionfSkipDefaultTransaction: true})
tx.Create(&user)
//方法2:默认批量导入会调用 Hooks 方法,使用SkipHooks’跳过
DB.Session(&gorm.SessionfSkipHooks: truer).Create(&users)
DB.Session(&gorm.SessionfSkipHooks: true?)CreateInBatches(users,1000)
// 方法3:使用 Prepared Statement
err := gorm.Open(sqlite.Open("gorm.db"), &gorm.ConfigiPrepareStmt: true})
db db.Create(&users)
//混合使用
tx := db.Session(&Session{
PrepareStmt: true, SkipDefaultTransaction: true, SkipHooks: true, CreateBatchSize: 1000.
})
tx.Create(&user)
分库分表:
水平分库:
- 每个库的结构都一样;
- 每个库的数据都不一样,没有交集;
- 所有库的并集是全量数据
水平分表:
- 每个表的结构都一样
- 每个表的数据都不一样,没有交集;
- 所有表的并集是全量数据;
// 使用传入数据分表
func Table0fYear(user *User, year int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
tableName := user.TableName() + strconv.Itoa(year)
return db .Table(tableName)
}
}
DB.Scopes(Table0fYear(user,2019)).Find(&users)
// SELECT * FROM users_2019;
// 使用传入数据分库 (同一个连接)
func Table0fOrg(user *User, dbName string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB "{
tableName := dbName +"."+user.TableName()
return db .Table(tableName)
}
}
DB. Scopes( Table0fOrg (user ,"org1")).Find(&users)
// SELECT * FROM org1.users;
// 使用对象信息获取表名 /interface
func TableOfUser(user *User) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB
year := getYearInfoFromUserID(user .ID)
return db .Table(user .TableName ) + strconv.Itoa( year))
}
数据库迁移:
通过AutoMigrate进行迁移,更加方便快捷
//自动迁移数据库
db.AutoMigrate(&User{})
//版本管理数据库
import "github.com/go-gormigrate/gormigrate/v2"
m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migrationt{
{
ID:"201608301400",
Migrate: func(tx *gorm.DB) error {
type User struct {
Name string
}
return tx.AddColumn(&User{},"Name")
},
Rollback: func(tx *gorm.DB) error {
return tx.DropColumn(&User{},"Name")
,.
},
})