当我在使用Go语言开发时,使用MySQL数据库,gorm框架,遇到了一些小坑,在这里给自己一个记录,也给各位道友普及一下我掉过的坑,有类似的可以借鉴一下。
在使用gorm时,会有这样的需求:
我想要存储数据,这条数据如果在数据库中存在,就做更新操作;如果不存在就做插入操作。
而gorm就提供了很好的封装——Save方法。
我们来看看官方文档怎么说这个Save的:
1.4.1. 更新全部字段
Save将包括执行更新SQL时的所有字段,即使它没有更改
直接说一个拓展的应用吧,基础应用就照猫画虎即可。
应用场景:用户的关注其他用户功能。单独维护一张关注表,可用来查找是否互相关注。
不论是关注者ID还是被关注者ID都不适合做主键,因为一个用户可被多个用户关注,一个用户可关注多个其他用户。
于是定义结构:
type UserFollow struct {
ID int64 `json:"id" gorm:"primary_key"`
AccountID int64 `json:"account_id"` //被关注者ID
FollowerID int64 `json:"follower_id"` //关注者ID
Status STATUS `json:"status" gorm:"default:1"` //是否有效
}
写入数据库代码:
f := new(UserFollow )
f.AccountID = 1
f.FollowerID = 2
f.Status = 1
db.NewConn().Model(f).Save(&f)
然而并没有实现有记录则更新的操作,一直在添加新的数据。这是为什么呢?
其实,这是因为,当你在使用Save 的时候,如果Save的对象包含主键,则能实现有记录则更新,无记录则插入;当对象不包含主键,则只有插入功能。 但是ID主键如果想要获取,那必须要查询一次数据库,既然查询数据库了,那还用Save干嘛。
既然单独的一个ID不能做主键,但是“我关注你”这样的一个状态,在表中的体现只可能是一条,于是想到了复合主键(联合主键、组合主键,个人爱怎么叫怎么叫吧),官方给出的文档:
1.5. 复合主键
将多个字段设置为主键以启用复合主键
type Product struct {
ID string `gorm:"primary_key"`
LanguageCode string `gorm:"primary_key"`
}
很好,简单明了,开搞:
type UserFollow struct {
AccountID int64 `json:"account_id" gorm:"primary_key"`
FollowerID int64 `json:"follower_id" gorm:"primary_key"`
Status STATUS `json:"status" gorm:"default:1"`
}
编译生成表:
if err := dbExPool.NewConn().AutoMigrate(
new(domain.UserFollow),
).Error; err != nil {
loggers.Error.Printf("Exchange DB auto migrate error %s", err.Error())
return
}
直接报错:
[2020-04-26 11:55:42] [35.15ms] CREATE TABLE `user_follows` (`account_id` bigint AUTO_INCREMENT,`follower_id` bigint AUTO_INCREMENT,`status` int DEFAULT 1 , PRIMARY KEY (`account_id`,`follower_id`))
[0 rows affected or returned ]
[ERROR] 2020/04/26 11:55:42 main.go:34: Exchange DB auto migrate error Error 1075: Incorrect table definition; there can be only one auto column and it must be defined as a key
“Error 1075: Incorrect table definition; there can be only one auto column and it must be defined as a key”
错误指出:只能有一项自动增长列!
注意看报错第一行的sql语句,我们设置的两个PRIMARY KEY都是“AUTO_INCREMENT”,这是因为,当没有指定的时候,gorm自动帮我们认定,主键int类为自动增长。所以需要我们手动更改,指定转化的sql语句 sql:"type:INT(10) UNSIGNED NOT NULL"
:
type UserFollow struct {
AccountID int64 `json:"account_id" gorm:"primary_key" sql:"type:INT(10) UNSIGNED NOT NULL"`
FollowerID int64 `json:"follower_id" gorm:"primary_key" sql:"type:INT(10) UNSIGNED NOT NULL"`
Status STATUS `json:"status" gorm:"default:1"`
}
这时再生成表:
[2020-04-26 12:02:36] [392.48ms] CREATE TABLE `user_follows` (`account_id` INT(10) UNSIGNED NOT NULL,`follower_id` INT(10) UNSIGNED NOT NULL,`status` int DEFAULT 1 , PRIMARY KEY (`account_id`,`follower_id`))
[0 rows affected or returned ]
Process finished with exit code 0
搞定!