这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记。
课程导学:【Go 语言原理与实践学习资料(下)】第三届字节跳动青训营 - 后端专场 - 掘金 (juejin.cn)
课程PPT:设计模式之 database sql与 GORM实践
前置知识
database/sql
在Go语言的标准库中,提供了进行数据库操作的database/sql库,需要注意的是在使用sql库的时候需要导入数据库驱动。
DSN
DSN全称为Data Source Name,数据名称具有通用的格式,但是没有类型前缀(由方括号标记的可选部分)
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
完整的DSN形式:
username:password@protocol(address)/dbname?param=value
GORM
goland写的ORM库,ORM即Object-Relational Mapping,即对象关系映射,这里Relational指的是关系型数据库,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。
具体的对象和数据库映射关系如下所示:
- 表(table) -> 类(class/struct)
- 记录(record, row) -> 对象 (object)
- 字段(field, column) -> 对象属性(attribute)
课程内容
一、理解 Database/SQL
参考资料:blog.csdn.net/random_w/ar…
-
Database/SQL 的基本用法
第一步 导入数据库引擎
常见的数据库引擎驱动见github.com/golang/go/w…
github.com/go-sql-driver/mysql/ github.com/mattn/go-sqlite3第二步 连接数据库
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1)/hello") if err != nil { panic(err) }第三步 设置连接参数
// 设置连接的生命周期,超过这个时间他会被Close,一般不会设置这个 db.SetConnMaxLifetime(time.Hour * 24) // 设置最大空闲连接池的大小,默认为2 db.SetMaxIdleConns(100) // 设置最大连接数量 db.SetMaxOpenConns(20)第四步 测试连接
// 通过 ping 测试数据库是否连接成功 if err := db.Ping(); err != nil { panic(err) }第五步 执行sql语句 sql语句有很多,这里仅仅举例子
rows,err := db.Query("select id, name from users where id = ?",1) // 执行一条sql,通过rows取出返回的数据 if err != nil{ // xxx } defer func(){ rows.Close() //处理完毕后需要释放连接 } var users []User for rows.Next(){ // Next准备用于Scan方法的下一行结果,如果成功返回true,如果没有下一行或者发生错误返回false,Err则是用来怒区分这两种情况的 var user User err := rows.Scan(&user.ID,&user.Name) // Scan用来将各行的结果填充到dest指定的各个值当中 if err != nil{ // xxx } users = append(users,user) } if rows.err != nil{ // xxx } -
Database/SQL 设计原理
在中间的database/sql层中,会对连接池进行操作,为了建立与数据库新的连接,可以直接从连接池中复用已存在的连接,或者直接新建新的连接,当当前连接使用完后,还会放回到连接池中
defer dc.db.putConn(dc,err,true)
二、GORM的基本使用
GORM是一个ORM库,在其他语言中典型的 ORM 库比如 Java 中的 Hibernate、Ruby 中的 ActiveRecord、以及 Laravel 中的 Eloquent。
GORM 的功能非常强大,除了基本的基于模型类对数据表进行增删改查之外,还支持定义关联关系、执行数据表迁移、查询链以及很多其他高级特性,并且支持在特定事件发生时(比如插入、更新、删除)触发指定的回调函数(类似 Laravel 框架的模型事件)。
GORM包相当于在database/sql包上加了一层
特点: 1.设计简洁、功能强大、自由扩展的全功能ORM; 2.使用ORM就可以不用再去拼接sql字符串了
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)/3306"))
var users []User
err := db.Select("id","name").Find(&user,1).Error
// 与上面database/sql的实现方式作对比,gorm明显简略很多
}
数据表自动迁移功能:通过 AutoMigrate 方法传入要迁移的模型类实例即可,GORM 会自动创建对应的数据表,表名规则是模型类名小写的复数形式。
db.AutoMigrate(&Product{})
db.Migrator().CreateTable(&Product{})
新增数据
user := User{Name:"JinZhu",Age:18,Birthday:time.Now()}
result := db.Create(&user)
user.ID // 返回主键
result.Error // 返回error
result.RowsAffected // 返回影响的行数
批量新增
var users = []User{{Name:"kieran"},{Name:"kieran2"},{Name:"kieran3"}}
db.Create(&users)
db.CreateInBatches(users,100)
基本用法CRUD
var product Product
db.First(&product,1) // 查询id为1的数据
db.First(&product,"code=?","L1212")
result := db.Find(&user,[]int{1,2,3})
result.RowsAffected
// 更新某个字段
db.Model(&product).Update("Price",2000)
// 更新多个字段
db.Model(&product).Update(Product{Price:2000,Code:"L1212"})
// 批量更新
db.Model(&Product{}).where("price < ?",2000).Update(map[string]interface{"Price":2000,"Code":"L1212"})
// 删除product
db.delete(&product)
模型定义
type User struct{
gorm.Model
ID uint
Name string
Email *string
Age uint8
Birthday *time.Time
}
关联操作——Preload、Joins预加载
type User struct{
Orders []Order
Profile Profile
}
db.Preload("Orders").Preload("Profile").Find(&users) // 查询用户的时候找出其订单和个人信息
级联删除
type User struct{
ID uint
Name string
Account Acount `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Orders []Card `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
db.Select("Orders","CreditCards").Delete(&user) // 删除user的同时,也删除user的orders和creditcards
db.Select(clause.Associations).Delete(&user) // 删除user的同时,也删除其依赖的所有many2many、one2many记录
三、GORM的设计原理
-
gorm插件介绍
首先讲下gorm语句执行过程:Finisher方法会决定Statement类型,再顺序执行该statement下的回调函数(callbacks),最后生成SQL并且执行 比如执行create语句,会依次执行下面七条回调函数
这样我们可以在以上流程中注册新的回调函数,或者删除、替换、改变回调顺序来实现插件功能
-
gorm插件的作用:1.多租户;2.多数据库,读写分离;3.加解密,混沌工程等
-
ConnPool:ConnPool是接口,DBConn是用来实现interface的,GO实际上是通过ConnPool接口来实现对数据库的交互