这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天
Gorm 的基本使用
提供了TableName的接口,返回表名
- 创建一个结构,作为数据库列名
type Product struct {
Id uint `gorm:"primary_key;AUTO_INCREMENT"` //实现主键、自增
Code string
Price uint
}
Gorm 提供了TableName的接口,返回表名
func (p Product) TableName() string {
return "product"
}
- 连接数据库
db, err := gorm.Open(
mysql.Open("name:password@tcp(localhost:3306)/databasename?charset=utf8mb4"),
&gorm.Config{}) //可以传一些参数
if err != nil {
panic("failer to connect database")
}
- 建表
创表下面二选一
- 根据Product结构体,自动创建表结构,如果存在会报错
db.Migrator().CreateTable(&Product{}) - 根据Product结构体,自动创建表结构,如果存在不会创建
db.AutoMigrate(&Product{})
- 删表
删除表 Product
b.Migrator().DropTable(&Product{})
- 插入
给 Product 表插入一条数据Code:"D42",Price:100
db.Create(&Product{Code: "D42", Price: 100})
- 查找
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)
- 更新
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)
- 删除
- 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 可以查询到被软删的数据。
- 关系
在数据库中,我们将一条 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")
}