这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
1 ORM框架-GORM
ORM 是 Object Relational Mapping 的缩写,译为“对象关系映射”,它解决了对象和关系型数据库之间的数据交互问题。提前配置好对象和数据库之间的映射关系,ORM 就可以自动生成 SQL 语句,并将对象中的数据自动存储到数据库中,整个过程不需要人工干预。
1.1 定义 gorm model
type Product struct {
ID uint
Code string
Price uint
}
1.2 为model定义表名
func (p Product) TableName() string {
return "product"
}
1.3 约定
默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。
1.4 连接数据库
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server。
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
1.5 创建数据
db.Create(&Product{Code : "D42", Price: 100})
p := &Product{Code: "A15", Price: 200}
result := db.Create(p)
p.ID // 返回插入数据的主键
result.Error // 返回error
result.RowsAffected // 返回插入记录的条数
// 创建多条
products := []*Product{{Code : "D41"},{Code : "D43"}}
result := db.Create(products)
可以通过default标签为字段定义默认值:
type Product struct {
ID uint
Code string `gorm:"default:galeone"`
Price uint `gorm:"default:100"`
}
1.6 查询数据
// 获取第一条记录(主键升序)
// 没有找到记录时,它会返回 ErrRecordNotFound 错误
p := &Product{}
// SELECT * FROM product ORDER BY id LIMIT 1;
db.First(p)
// 查询多条数据
products := make([]*Product, 0)
// SELECT * FROM product WHERE price > 10;
result := db.Where("price > 100").Find(&products)
result.Error // returns error
result.RowsAffected // 返回找到的记录数,相当于len(products)
// SELECT * FROM product WHERE code IN ('D41', 'D43');
db.Where("code IN ?", []string{"D41", "D43"}).Find(&products)
// SELECT * FROM product WHERE code LIKE 'D%';
db.Where("code LIKE ?", "D%").Find(&products)
// SELECT * FROM product WHERE code = 'D41' AND price >= 100;
db.Where("code = ? AND price >= ?", "D41", "100").Find(&products)
当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果字段值为 0、''、false 或其他零值,该字段不会被用于构建查询条件。例如:
// SELECT * FROM product WHERE code = 'D41';
db.Where(&Product{Code: "D41", Price: 0}).Find(&products)
可以使用map来构建查询条件,如:
// SELECT * FROM product WHERE code = 'D41' AND price = 0;
db.Where(map[string]interface{}{"Code": "D41", "Price": 0}).Find(&products)
1.7 更新数据
Update用于更新单列,当使用了 Model 方法,且该对象主键有值,该值会被用于构建条件,例如:
// 条件更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
Updates用于更新多列,当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段,如果想确保指定字段被更新,应该使用 Select 更新选定字段,或使用 map 来完成更新操作。例如:
// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// 使用Select
// Select 和 Map
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello' WHERE id=111;
// Select 和 Struct (可以选中更新零值字段)
DB.Model(&result).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;
GORM还允许使用SQL表达式更新,例如:
// product 的 ID 是 `3`
db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
// UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;
1.8 删除数据
- 物理删除
// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;
// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";
db.Delete(&User{}, 10) // DELETE FROM users WHERE id = 10;
db.Delete(&User{}, "10") // DELETE FROM users WHERE id = 10;
db.Delete(&users, []int{1,2,3}) // DELETE FROM users WHERE id IN (1,2,3);
- 软删除
如果模型包含一个gorm.DeletedAt字段(gorm.Model已经包含),它将自动获得软删除的能力。拥有软删除能力的模型调用Delete时,记录不会从数据库中真正删除。但GORM会将DeletedAt置为当前时间,并且通过正常的查询方法找不到该记录,可以使用Unscoped找到被软删除的记录。
// user 的 ID 是 `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
// 批量删除
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
// 在查询时会忽略被软删除的记录
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;
1.9 事务
GORM提供了Begin、Commit、Rollback方法用于使用事务。
// 开始事务
tx := db.Begin()
// 在事务中执行一些 db 操作(应该使用 'tx' 而不是 'db')
tx.Create(...)
// ...
// 遇到错误时回滚事务
tx.Rollback()
// 否则,提交事务
tx.Commit()
GORM提供了Transaction方法自动提交事务,避免用户漏写Commit、Rollback。
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,使用 'tx' 而不是 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
1.10 Hook
Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。如果为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。钩子方法的函数签名为 func(*gorm.DB) error。