GORM的使用入门
触发器(Hook)
Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。
假如说,我们想在一条记录插入到数据库之前,先打印出插入的内容
type User struct {
Name string
Age int
Address *string
}
func (user *User) BeforeCreate(tx *gorm.DB) (err error) {
address := fmt.Sprintf("%s' home", user.Address)
user.Address = &address
return nil
}
可用的触发器
BeforeCreate在插入记录之前AfterCreate在插入记录之后BeforeUpdate在更新记录之前AfterUpdate在更新记录之后BeforeSave在保存记录之前AfterSave在保存记录之后BeforeDelete在删除记录之前AfterDelete在删除记录之后AfterFind/AfterFirst/AfterTake/AfterLast在查询记录之后
注意:在触发器运行出错报错之后,如果是对数据库造成修改的操作,会产生事务性的回滚;查询等不会影响数据库的操作,便不会触发回滚。
外键
一对多关系
一对多关系在现实中的应用很常见,例如关注列表,视频发布列表等等都是一对多的关系。Gorm中,一对多关系有两类
Belongs To属于Has Many拥有
我们以用户发布的视频列表为例,一个用户可以发布多个视频,但每个视频只能拥有一个作者。
type User struct {
ID uint `
Name string
Videos []Video // 用户发布的视频
}
type Article struct {
ID uint
Title string
UserID uint
User User
}
上面的代码片段每一个Article就属于一个User,而UserID便是外键 注意:被引用的类型要和引用的外键类型一致,包括大小。外键名称就是关联表名加上引用者的类型名。
如果想自定义外键,也可以对外键进行重写,可以使用gorm:"foreignKey:xxx"进行自定义外键,如
type User struct {
ID uint
Name string
Videos []Video `gorm:"foreignKey:UID"`// has many关系
}
type Video struct {
ID uint
Title string
UID uint
User User `gorm:"foreignKey:UID"` // belongs to关系
}
Gorm通一般使用被引用者的主字段作为外键的参照,如果想修改,Gorm也提供了独特的字段帮助你进行修改。gorm:"references:xxx"。
type User struct {
ID uint
Name string
Videos []Video `gorm:"foreignKey:UserName;references:Name"` // has many关系
}
type Video struct {
ID uint
Title string
UserName string
User User `gorm:"references:Name"` // belongs to关系
}
插入记录
如果需要创建用户的时候,同时创建他发布的视频。
v1 := Video{Title: "xxx1"}
v2 := Video{Title: "xxx2"}
user := User{Name: "xxx", Videos: []Video{v1, v2}}
DB.Create(&user)
Gorm会自动将他们关联。
如果用户已存在,需要添加视频,并且关联已有的用户。
v1 := Video{Title: "xxx1", UserID: 3}
DB.Create(&v1)
或者
var user User
DB.Take(&user, 3)
DB.Create(&Article{Title: "xxx1", User: user})
如果用户和视频都已经存在,需要添加外键,可以使用Save方法或者Append方法。
查询记录
如果想查询用户,并显示其视频列表,直接用Take/First/Last方法是不行的。必须要使用Gorm提供的预加载方法来加载文章列表Preload。
var user User
DB.Preload("Videos").Take(&user, 3)
其中,预加载的名称就是外键关联的属性名。 同理,如果需要查询视频,并显示它的作者信息,也需要使用预加载功能。
var video Video
DB.Preload("User").Take(&video, 4)
更高级的用法是:如果需要查询视频显示其作者信息,并且显示作者所有的视频,则需要嵌套预加载。如 DB.Preload("User.Videos").Take(&video, 3)。也可以在预加载中附带条件信息,如必须要满足视频ID小于300,DB.Preload("Articles", "id < ?", 300).Take(&user, 1)。
删除记录
删除用户的同时如果同时删除名下所有视频,即级联删除,可使用DB.Select("Videos").Delete(&user)。如果只是想删除用户,而只将其视频的外键置为空。
DB.Preload("Videos").Take(&user, 3)
DB.Model(&user).Association("Videos").Delete(&user.Videos)
一对一关系
一对一关系使用的场景一般较少,比较常见的应用场景是用于表的拓展。比如说,我们可以将一个用户表拆分成两个部分,常常需要查询修改的部分放在主表当中,长时间不需要变动的部分放置在副表当中,这样便可以提高查询效率。
type User struct {
ID uint
Name string
Age int
UserInfo UserInfo // 通过UserInfo可以拿到用户详情信息
}
type UserInfo struct {
UserID uint // 外键
ID uint
Addr string
Like string
}
插入记录
假如说我们定义了一对一关系之后,插入员工记录
DB.Create(&User{
Name: "xxx",
Age: 3,
UserInfo: UserInfo{
Addr: "Beijing",
Like: "sleep",
},
})
也可以通过修改结构体结构进行副表的添加。例如修改副表
type UserInfo struct {
User *User
UserID uint
ID uint
Addr string
Like string
}
注意:副表需要传递指针,否则会因为嵌套引用而发生报错。修改成这样之后,就可以通过下列方式进行记录的添加
var user User
DB.Take(&user, 5)
DB.Create(&UserInfo{
User: &user,
Addr: "Bejing",
Like: "sleep",
})
和一对多关系类似,我们也可以通过在gorm控制标签中添加标签来重写外键和引用。
多对多关系
假设每个用户都可以发布多个视频,视频也可以多个用户联合发布,则用户和视频可以构成多对多关系。
type User struct {
ID uint
Name string
Videos []Video `gorm:"many2many:video_users;"`
}
type Video struct {
ID uint
Title string
Users []User `gorm:"many2many:video_users;"`
}
两个结构体都有标签适用于反向引用。
插入记录
DB.Create(&Video{
Title: "xxx1",
Users: []User{
{Name: "xxx"},
{Name: "yyy"},
},
})
如果需要自定义外键,操作方法和一对多的方法差不多。