GORM与database/sql入门学习 | 字节后端青训营笔记

382 阅读5分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第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&...&paramN=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 设计原理

    image.png 在中间的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的设计原理

image.png

  • gorm插件介绍

    首先讲下gorm语句执行过程:Finisher方法会决定Statement类型,再顺序执行该statement下的回调函数(callbacks),最后生成SQL并且执行 比如执行create语句,会依次执行下面七条回调函数 image.png 这样我们可以在以上流程中注册新的回调函数,或者删除、替换、改变回调顺序来实现插件功能

  • gorm插件的作用:1.多租户;2.多数据库,读写分离;3.加解密,混沌工程等

  • ConnPool:ConnPool是接口,DBConn是用来实现interface的,GO实际上是通过ConnPool接口来实现对数据库的交互

    image.png