GORM连接数据库 | 青训营

85 阅读4分钟

Gorm

引入

  1. 安装

go get -u gorm.io——导入gorm
go get -u gorm.io/driver/mysql ——导入mysql引擎

  1. 创建数据库、表及设定好属性

CREATE DATABASE dbgorm

CREATE TABLE products(
`code` VARCHAR(50),
price FLOAT)
  1. 导入依赖包
import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)
  1. 设定操作的结构体(表的映射对象,表是复数如products,结构体单数Product)
type Product struct {
	Code  string
	Price float32
}
  1. 为结构体定义表名(可有可无)
func (p Product) TableName() string{
    return "product"
}
  1. 连接数据库
dsn := "root:ann@tcp(127.0.0.1:3306)/dbgorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
	panic("failed to connect database")
}
  1. 创建数据
//创建一条记录
db.Create(&Product{Code: "D42", Price: 100})   //方式一
p := &Product{Code: "D41", Price: 100}
res := db.Create(p)      //方式二
//创建多条
products := []*Product{{Code: "D43", Price: 100}, {Code: "D44", Price: 100}}
db.Create(products)
  1. 如何使用 Upsert ——使用 clause.OnConflict处理数据冲突(以创建单条方式二为例)

db.Clauses(clause.OnConflict{DoNothing: true}).Create(&p)

  1. 如何使用默认值——通过 default 标签为字段定义默认值(在定义model时使用)

name string gorm:"default:ann"``

  1. 查询数据
//查询第一个,并显示
p := Product{}
result := db.First(&p)//排序第一个
fmt.Println(p)
//查询多个,并显示受影响行数
//指定查询条件
products := make([]*Product, 0)
result = db.Where("code IN ?", []string{"D42", "D41"}).Find(&products) //元组范围查询
result = db.Where("price >= 100").Find(&products)                      //范围查询
result = db.Where("code LIKE ?", "D4%").Find(&products)                //模糊查询
result = db.Where("code = ? AND price >= ?", "D42", 100).Find(&products) //明确查询
//指定查询的对象
result = db.Where(&Product{Code: "D41", Price: 0}).Find(&products)                     //结构体(0/false的字段会被忽略)
result = db.Where(map[string]interface{}{"Code": "D41", "Price": 100}).Find(&products) //map
fmt.Println(result.RowsAffected)
  1. 使用First时,需要注意查询不到数据会返回 ErrRecordNotFound
  2. 使用Find查询多条语句,查询不到数据不会返回错误
  3. 使用结构体作为查询条件时,GORM只会查询非零值字段(即字段值为0/false等,该字段不会用于构建查询条件,使用Map来构建查询条件)
  4. 更新数据
    1. 满足条件的单列全都改变(model指定数据库,where指定更新的范围,update指定更新内容)
    2. 如果db.Model(&Product{id: "D42"})里面是主键,则相当于加了where id ="D42",如果里面不是主键,则必须要加where子句作为查询条件
//修改单列(update)
db.Model(&Product{Code: "D42"}).Where("price >= 100").Update("price", 200) 
db.Model(&p).Where("code = ?", "D42").Update("price", 700) //单列
//修改多列(updates)
db.Model(&p).Where("code = ?", "D44").Updates(Product{Code: "D40", Price: 500}) //多列
//根据`map`更新属性(Updates)
db.Model(&p).Where("code = ?", "D41").Updates(map[string]interface{}{"Code": "D41", "Price": 700})
db.Model(&p).Where("code = ?", "D45").Select("code").Updates(map[string]interface{}{"Code": "D43", "Price": 800})
  1. 使用Struct更新时,只会更新非零值,如果需要更新零值可以使用Map更新或使用Select选择字段
  2. 删除数据
//物理删除
//用主键删除
db.Delete(&p,1)
db.Delete(&Product{},2)
//查询删除
db.Delete(Product{}, "code = ?", "D40")
db.Where("code = ?", "D43").Delete(Product{})
//软删除(结构体中定义Deleted gorm.DeletedAt)
p := Product{Code: "D42", Price: 100}
db.Delete(&p) //删一条
db.Where("code = ?", "D41").Delete(&Product{})//批量删除
//查询(普通查询不行)
products := make([]*Product, 0)
db.Unscoped().Where("code = ?", "D41").Find(&products)
  1. GORM 提供了gorm.DeletedAt用于帮助用户实现软删除,在model结构体中定义Deleted gorm.DeletedAt
  2. 拥有软删除能力的Model调用Delete时,记录不会被从数据库中真正删除,但GORM会将DeletedAt置为当前时间,并且你不能再通过正常的查询方法找到该记录
  3. 使用Unscoped可以查询到被软删的数据
  4. 默认约定
  5. Gorm 使用名为 ID 的字段作为主键
  6. Gorm 使用结构体的蛇形复数(小写+下划线+复数)Product——>products
  7. 字段名的蛇形作为列名
  8. 使用CreatedAtUpdatedAt字段作为创建、更新时间
  9. 支持的数据库(根据数据库的选择决定连接时 dsn格式)
  10. MySQL
  11. SQLServer
  12. PostgreSQL
  13. SQLite

操作详解

  1. 事务
    1. GORM提供了Begin、Commit、Rollback方法用于使用事务
tx := db.Begin()//开始事务
if err = tx.Create(&Product{Code: "D41"}).Error; err != nil{
    //事务中处理都用tx,而不是db
    tx.Rollback  //遇到错误回滚
    return
}
tx.Commit() //提交事务
  1. GORM 提供了Transaction 方法用于自动提交事务,避免用户漏写Commit、Rollback
if err = db.Transaction(func(tx *grom.DB) error {
    if err = tx.Create(&Product{Code: "D41"}).Error; err != nil{
    	return err
    }
    return nil
}); err != nil {
    return
}
  1. Hook
    1. GORM 提供了CURD 的Hook能力
    2. Hook是在创建、查询、更新、删除等操作之前、之后自动调用的函数
    3. 如果任何Hook返回错误,GORM将停止后续的操作并回滚事务

拓展

  1. 性能提高
dsn := "root:ann@tcp(127.0.0.1:3306)/dbgorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    SkipDefaultTransaction: ture,  //关闭默认事务
    PrepareStmt: true              //缓存预编译语句
})
if err != nil {
	panic("failed to connect database")
}
  1. 对于写操作(创建、更新、删除),为了确保数据的完整性,GORM会将他们封装在事务内运行,但是这也会降低性能——使用SkipDefaultTransaction关闭默认事务
  2. 提高后续调用的速度——使用PrepareStmt缓存预编译语句
  3. 生态(常用扩展如下) | GORM 代码生成工具 | github.com/go-gorm/gen | | --- | --- | | GORM 分片库方案 | github.com/go-gorm/sha… | | GORM 手动索引 | github.com/go-gorm/hin… | | GORM 乐观锁 | github.com/go-gorm/opt… | | GORM 读写分离 | github.com/go-gorm/dbr… | | GORM OpenTelemetry 扩展 | github.com/go-gorm/ope… |