gorm使用规范和最佳实践,仅做参考
更新
在对数据库进行更新的时候,最需要注意的就是Model方法,因为model方法中如果主键值为默认零值,则会对全表进行更新!例如:
func foo(){
db.Model(&user).Update("name", "hello")
}
如果此时user.id为零,则会对user的name列全部更新为hello 因此强制要求在update前进行where子句的拼接,即使user存在主键:
func foo(){
db.Model(&user).Where("id = ?",user.id).Update("name", "hello")
}
此时如果user为零值,则会返回RecordNotFound未找到记录的错误,而不会对全表进行更新
自动迁移
AutoMigrate会自动迁移数据库相关schema表,一般会写在model层的init函数中:
func init(){
db.AutoMigrate(&User{})
}
在init时会自动创建user表/增加列,如果user表不存在的话。 强烈建议不使用autoMigrate,最佳实践应当是对于新增列和表,使用sql单独记录,便于数据库记录追溯,便于我们掌握建表时的行为。因为explicit is always better than implict,所以sql应当单独提取出来一个库进行维护。
模型声明
对于匿名字段,gorm会自动将结构体字段内嵌 建议每张表都维护自定义结构体,以提取公共字段,值得注意的是,DeletedAt必须建立索引,以防止软删除时进行全表扫描:
type MyModel struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
错误处理
gorm的一个比较有争议的特性就是,没有找到结果/记录,是否应该当作一个错误来进行处理? gorm的实现导致我们必须要人为区分真正在查询过程中出现的错误和未找到记录的错误:
if err = db.Find().Error;err != nil{
if err == gorm.ErrRecordNotFound{
...
}
...
}
偏题大概说一下这个问题,在gorm中将结果查询到slice中是不会返回RecordNotFound的,因为只需要判断slice的长度是否为空就可以判断是否找到记录,但是如果是结构体的话,无法通过某一个字段或者结构体指针为nil来判断是否找到结果,因为必须要在db.Find(&user)前对user初始化。gorm在这点上完全把未找到当作一个错误对待,导致了业务层面对错误处理的混乱,每次都需要单独判断err的类型是非常不优雅的行为。 在model层可以通过对结果赋值nil来进行区分:
func (ctl *StudentCtl) QueryUser()(*User,error){
...
if err == gorm.ErrRecordNotFound{
return nil,nil
} else {
return nil,err
}
...
return user,nil
}