一、 钩子
钩子是一个在 插入 / 查询 / 更新 / 删除 之前或之后被调用的方法。
在一个模型中定义特殊的方法,它将会在插入,更新,查询,删除的时候被自动调用,如果任何的回调抛出错误,GORM 将会停止将要执行的操作并且回滚当前的改变。
// 开启事务
BeforeSave
BeforeCreate
// 连表前的保存
// 更新时间戳 `CreatedAt`, `UpdatedAt`
// 保存自己
// 重载哪些有默认值和空的字段
// 链表后的保存
AfterCreate
AfterSave
// 提交或回滚事务
Example:
func (u *User) BeforeSave() (err error) {
if u.IsValid() {
err = errors.New("can't save invalid data")
}
return
}
func (u *User) AfterCreate(scope *gorm.Scope) (err error) {
if u.ID == 1 {
scope.DB().Model(u).Update("role", "admin")
}
return
}
// 在 GORM 中的保存 / 删除 操作会默认进行事务处理,所以在事物中,所有的改变都是无效的,直到它被提交为止:
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
tx.Model(u).Update("role", "admin")
return
}
更新对象
// 开启事务
BeforeSave
BeforeUpdate
// 链表前的保存
// 更新时间戳 `UpdatedAt`
// 保存自身
// 链表后的保存
AfterUpdate
AfterSave
// 提交或回滚的事务
func (u *User) BeforeUpdate() (err error) {
if u.readonly() {
err = errors.New("read only user")
}
return
}
// 在事务结束后,进行更新数据
func (u *User) AfterUpdate(tx *gorm.DB) (err error) {
if u.Confirmed {
tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("verfied", true)
}
return
}
删除对象:可用于删除的钩子
// 开启事务
BeforeDelete
// 删除自身
AfterDelete
// 提交或回滚事务
// 在事务结束后进行更新数据
func (u *User) AfterDelete(tx *gorm.DB) (err error) {
if u.Confirmed {
tx.Model(&Address{}).Where("user_id = ?", u.ID).Update("invalid", false)
}
return
}
查询对象:可用于查询的钩子
// 从数据库中读取数据
// 加载之前 (急于加载)
AfterFind
func (u *User) AfterFind() (err error) {
if u.MemberShip == "" {
u.MemberShip = "user"
}
return
}
二、事务
GORM 默认在事务中执行单个 create, update, delete 操作,以确保数据库数据完整性。如果需要将多个 create, update, delete 当成一个原子性操作,Transaction 就是为了这个而创造的。
事务流程:
// 开启事务
tx := db.Begin()
// 在事务中执行一些数据库操作 (从这里开始使用 'tx',而不是 'db')
tx.Create(...)
// ...
// 发生错误回滚事务
tx.Rollback()
// 或者提交这个事务
tx.Commit()
三、运行原生SQL
执行原生SQL时不能使用链式调用其他方法
db.Exec("DROP TABLE users;")
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now(), []int64{11,22,33})
// Scan
type Result struct {
Name string
Age int
}
var result Result
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
使用 *sql.Row 或者 *sql.Rows 获得查询结果
row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
...
rows.Scan(&name, &age, &email)
...
}
// 原生SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
...
rows.Scan(&name, &age, &email)
...
}
扫描 sql.Rows 数据到模型
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
var user User
// ScanRows 扫描一行到 user 模型
db.ScanRows(rows, &user)
// do something
}
四、数据库接口
GORM 提供了从当前的 *gorm.DB 连接中返回通用的数据库接口的方法 DB *sql.DB 。
// 获取通用数据库对象 sql.DB 来使用他的 db.DB() 方法
// Ping
db.DB().Ping()
// 连接池
// SetMaxIdleConns 设置空闲连接池中的最大连接数。
db.DB().SetMaxIdleConns(10)
// SetMaxOpenConns 设置数据库连接最大打开数。
db.DB().SetMaxOpenConns(100)
// SetConnMaxLifetime 设置可重用连接的最长时间
db.DB().SetConnMaxLifetime(time.Hour)
五、自定义Loggor(感觉没啥用)
// 开启 Logger, 以展示详细的日志
db.LogMode(true)
// 关闭 Logger, 不再展示任何日志,即使是错误日志
db.LogMode(false)
// 对某个操作展示详细的日志,用来排查该操作的问题
db.Debug().Where("name = ?", "jinzhu").First(&User{})
例如,使用 Revel 的 Logger 作为 GORM 的输出
db.SetLogger(gorm.Logger{revel.TRACE})
// 使用 os.Stdout 作为输出
db.SetLogger(log.New(os.Stdout, "\r\n", 0))