写作目的
自己在使用GORM时,感觉到有些力不从心(我必须得按照它的Model定义来使用),感觉好像只能用它做项目必须得一开始就使用它,而不能中途使用。希望能够自定义的方式使用GORM,不使用它提供的自动迁移,于是记录下来。
需求
现在我突然接收了别人的项目(可能是一个java项目),然后需要用golang实现数据库操作,别人的数据库不能改动,那么我就需要自定义Model一个一个适配。
案例
官网地址:gorm.io/docs/index.…
官网github:github.com/go-gorm/gor…
demo地址:github.com/BuddyXiao/g…
表
假如说目前已经有了三个表,分别是t_user,t_role和t_user_role,在po包中可以看到相应的结构体。
关联关系
多对多关系:
无法使用预加载的功能特性。
// 查询用户和该用户的所有角色
func TestAssociation(t *testing.T) {
dao.Init()
var user = po.User{}
user.ID = 1563001523807162370
var roleIds []int
dao.DB.Model(&po.UserRole{}).Where("user_id = ?", user.ID).Select("role_id").Find(&roleIds)
var roles []po.Role
dao.DB.Where("deleted = 0").Find(&orles, roleIds)
dao.DB.First(&user)
user.Roles = roles
//dao.DB.Preload("Roles").Where("deleted = 0").Find(&user) 无法使用预加载
fmt.Println(user)
}
Clause的使用
这个是GORM用来创建SQL语句的神器。在官方的clause包可以看到许多测试用例。
在钩子函数中实现软删除
这种方式的软删除,在查询是还得加上deleted=0的限制
以下是拼接一个UPDATE的语句:
func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
clauses := []clause.Interface{
clause.Update{},
clause.Set([]clause.Assignment{{Column: clause.Column{Name: "deleted"}, Value: 100}}),
clause.Where{Exprs: []clause.Expression{clause.Eq{Column: clause.PrimaryColumn, Value: u.ID}}},
}
for _, c := range clauses {
tx.Statement.AddClause(c)
}
tx.Statement.Build("UPDATE", "SET", "WHERE")
fmt.Println(tx.Statement.SQL.String()) // UPDATE `t_user` SET `deleted`=? WHERE `t_user`.`id` = ?
return
}
测试:这里需要传递user对象,将id传给BeforeDelete函数的接收者
func TestDeleteSimple(t *testing.T) {
dao.Init()
id := 1563001523807162373
var user po.User
user.ID = id
result := dao.DB.Delete(&user)
fmt.Println(result.Statement.SQL.String())
fmt.Println(result.RowsAffected)
fmt.Println(result.Error)
}
使用技巧
- 查看中间生成的SQL语句
db := dao.DB.Session(&gorm.Session{DryRun: true})
s1 := db.Model(&po.UserRole{}).Where("user_id = ?", user.ID).Select("role_id").Find(&orleIds).Statement
fmt.Println("orleIds:", s1.SQL.String())
// SELECT `role_id` FROM `t_user_role` WHERE user_id = ?
坑
- 在go结构体与数据库表做映射时,必须要将go结构体中属性字段的Tag标签中加入
gorm:"column:xxxx", 不然可能出现查询不到结果的情况。