青训营笔记

59 阅读4分钟

后端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")
	,.
	},
})