Gorm 的基本使用 | 青训营笔记

77 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天

Gorm 的基本使用

提供了TableName的接口,返回表名

  1. 创建一个结构,作为数据库列名
type Product struct { 
    Id    uint `gorm:"primary_key;AUTO_INCREMENT"` //实现主键、自增
    Code  string
    Price uint
}

Gorm 提供了TableName的接口,返回表名

func (p Product) TableName() string { 
    return "product"
}
  1. 连接数据库
db, err := gorm.Open(
    mysql.Open("name:password@tcp(localhost:3306)/databasename?charset=utf8mb4"),
    &gorm.Config{}) //可以传一些参数
if err != nil {
    panic("failer to connect database")
}
  1. 建表

创表下面二选一

  • 根据Product结构体,自动创建表结构,如果存在会报错 db.Migrator().CreateTable(&Product{})
  • 根据Product结构体,自动创建表结构,如果存在不会创建 db.AutoMigrate(&Product{})
  1. 删表

删除表 Product

b.Migrator().DropTable(&Product{})
  1. 插入

给 Product 表插入一条数据Code:"D42",Price:100

db.Create(&Product{Code: "D42", Price: 100})
  1. 查找

First 查找,只能找一条数据,没找到会报错,First找到第一条满足条件的句子

要传指针,才能把数据写进变量

  • 不指定任何类型就是根据整型主键查找 db.First(&product, 1)
  • 查找code字段值为D42的记录 db.First(&product, "code = ?", "D42")
  • 查找id大于8的第一条数据 db.Where("id>8").First(&product)

** Find 查找多条数据 ** ,找不到不会报错

var products []Gorm.Product
//SELECT * FROM product WHERE id > 8;
result := db.Where("id > 8").Find(&products)
fmt.Println(result.RowsAffected) //返回找到的记录数,相当于len(products)
fmt.Println(result.Error)        //返回error
fmt.Printf("%+v", products)
//SELECT * FROM product WHERE price IN (100, 200);
db.Where("price IN ?", []int{100, 200}).Find(&products)
fmt.Printf("%+v", products)
//SELECT * FROM product WHERE price LIKE '%0%';
db.Where("price LIKE ?", "%0%").Find(&products)
fmt.Printf("%+v", products)
//SELECT * FROM product WHERE id >= 5 AND price >= 300;
db.Where("id >= ? AND price >= ?", "15", "300").Find(&products)
fmt.Printf("%+v", products)

结构体查找

//使用结构体查询
//SELECT * FROM product WHERE id = 5
db.Where(&Gorm.Product{Id: 5, Price: 0}).Find(&products) //会有0值找不到的问题,要用下面的map
fmt.Printf("%+v", products)
//SELECT * FROM product WHERE id = 5 AND Price = 0
db.Where(map[string]interface{}{"Id": 5, "Price": 0}).Find(&products)
fmt.Printf("%+v", products)
  1. 更新

db.Model()是设置表名,也是制定表名。。 

//Update 将product的price更新为200
db.Model(&product).Update("Price", 200)
​
db.Model(&product).Updates(Product{Price: 200, Code: "F42"})                    //仅更新非零值字段,结构体没有赋值的值是0
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) //map可以更新0值

没有存在表名的时候需要用db.Model()

var product Gorm.Product
//Update product SET price=10 WHERE id>=12
//下面这个语句没有提到表名,所以要声明表名&Gorm.Product{}
db.Model(&Gorm.Product{}).Where("id >= ?", 12).Update("price", 10)
//查找最后一个,看看修改没
//db.Last(&product)
//fmt.Printf("%+v", product)
//更新多个列
//Update product SET Code="P12", Price="120" WHERE Id=12
//如果没有Where语句就用Model里的结构定Where,结构体0值直接忽视,用下面map
db.Model(&Gorm.Product{Id: 12}).Updates(Gorm.Product{Code: "P12", Price: 120})
//db.Last(&product)
//fmt.Printf("%+v", product)
//Update product SET Code="U12", Price="123" WHERE Id=12
db.Model(&Gorm.Product{Id: 12}).Updates(map[string]interface{}{"Code": "U12", "Price": 123})
//db.Last(&product)
//fmt.Printf("%+v", product)
//更新选定字段,下面只更新Code
//Update product SET Code="T12" Where Id=12
db.Model(&Gorm.Product{Id: 12}).Select("Code").Updates(map[string]interface{}{"Code": "T12", "Price": 1230})
//db.Last(&product)
//fmt.Printf("%+v", product)
//SQL 表达式更新
//Update "product" SET "price" = price*2+10 Where Id=12
db.Model(&Gorm.Product{Id: 12}).Update("Price", gorm.Expr("Price * ? + ?", 2, 100))
db.Last(&product)
fmt.Printf("%+v", product)
  1. 删除
  • Delete 删除id=1的 product db.Delete(&product, 1)
  • 批量删除 db.Delete(&product, "code = ?", "F42")
//DELETE FROM product WHERE id=10
db.Delete(&Gorm.Product{}, 10) //默认的是主键
//DELETE FROM product WHERE id IN (8, 9)
db.Delete(&Gorm.Product{}, []int{8, 9})
//DELETE FROM product WHERE Price LIKE "%6"
db.Where("Price LIKE ?", "%6").Delete(&Gorm.Product{})
//DELETE FROM product WHERE ID=11
db.Where("Id = ?", 11).Delete(&Gorm.Product{})
//DELETE FROM product WHERE ID=9
db.Delete(&Gorm.Product{}, "Id = ?", 9)

还可以用软删除,只需要在定义结构的时候多加一个变量Deleted gorm.DeleteAt 拥有软删除能力的 Model 调用Delete 时,记录不会从数据库中真正删除。但 Gorm 会讲 DeletedAt 置为当前时间,并且你不能再通过正常的查询方法找到该记录。

使用 Unscoped 可以查询到被软删的数据。

  1. 关系

在数据库中,我们将一条 SQL 语句称为一次基本的操作。将若干条 SQL 语句“打包”在一起,共同执行一个完整的任务,这就是事务。

事务( Transaction)由一次或者多次基本操作构成,或者说,事务由一条或者多条 SQL 语句构成。

基础的写法,有可能会因为忘记写Rollback、Commit而导致数据库出现问题

//开始事务
tx := db.Begin()
//在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
if err = tx.Create(&Gorm.Product{Code: "Y12", Price: 98}).Error; err != nil {
    tx.Rollback()
    //遇到错误时回滚错误
    return
}
if err = tx.Create(&Gorm.Product{Code: "R12", Price: 108}).Error; err != nil {
    tx.Rollback()
    return
}
//提交事务
tx.Commit()

较好的写法,不用写Rollback、Commit。

//开始事务,Gorm提供了Transaction方法用于自动提交事务,也不需要写RollBack,避免用户漏写Commit、RollBack
if err = db.Transaction(func(tx *gorm.DB) error {
    if err = tx.Create(&Gorm.Product{Code: "J12", Price: 189}).Error; err != nil {
        return err
    }
    if err = tx.Create(&Gorm.Product{Code: "K00", Price: 287}).Error; err != nil {
        return err
    }
    return nil
}); err != nil {
    return
}

Gorm 的约定(默认)

  • Gorm 使用结构体中名为 ID 的字段作为主键
  • 使用结构体的蛇形复数作为表名
  • 字段名的蛇形作为列名
  • 使用 CreatedAt、UpdatedAt 字段作为创建、更新时间

Gorm 的 Hook

Gorm 在提供了 CURD 的 Hook 能力。

Hook 是在创建、查询、更新、删除等操作之前、之后自动调用的函数。

如果任何 Hook 返回错误,GORM 将停止后续的操作并回滚事务。

Gorm 性能提高

对于写操作(创建、更新、删除),为确保数据的完整性,Gorm 会将它们封装在事务内运行。但这会降低性能,你可以使用 SkipDefaultTransaction 关闭默认事务。

使用 PrepareStmt 缓存预编译语句可以提高后续调用的速度,本机测试提高大约 35% 左右。

db, err := gorm.Open(mysql.Open(dsn),
    &gorm.Config{
        SkipDefaultTransaction: true, //关闭默认事务
        PrepareStmt:            true, //缓存预编译语句
    })
if err != nil {
    panic("failed to connect database")
}