适用于先读后更新的数据竞争场景,且应该将加锁操作放到事务中,防止锁被自动释放,原因参考mysql doc
func UpdateUser(db *gorm.DB, id int64) error {
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return err
}
user := User{}
// 锁住指定 id 的 User 记录
if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&user, id).Error; err != nil {
tx.Rollback()
return err
}
// 更新操作...
// commit事务,释放锁
if err := tx.Commit().Error; err != nil {
return err
}
return nil
}
sync.Mutex解法(效率较低):
var lock sync.Mutex
func UpdateUser(db *gorm.DB, id int64) error {
lock.Lock()
// 数据库操作...
lock.Unlock()
return nil
}
参考
doc
补充:Golang数据库编程之GORM模型定义与数据库迁移
在开发应用程序时,一般而言,我们是先设计好数据表,再使用开发语言建立对应的数据模型,不过,我们今天要讲的是一个逆向操作的过程,即如何通定义GORM框架的数据模型,然后再通过执行GROM框架编写的应用程序,用定义好数据模型在数据库中创建对应的数据表。
因此需要先讲讲怎么定义GORM的数据模型。
模型定义
一般来说,我们说GROM的模型定义,是指定义代表一个数据表的结构体(struct),然后我们可以使用GROM框架可以将结构体映射为相对应的关系数据库的数据表,或者查询数据表中的数据来填充结构体,如下所示,我们定义了一个名为Post的结构体。
type Post struct {
PostId int
Uid int
Title string
Content string
Type int
CreatedAt time.Time
UpdatedAt time.Time
}
创建好一个结构体只是第一步,不过先不着急要怎么去创建数据表,我们要先了解一下结构体与数据表之间的映射规则,主要有以下几点:
Struct tags
我们知道,Go语言的结构体支持使用tags为结构体的每个字段扩展额外的信息,如使用标准库encoding/json包进行JSON编码时,便可以使用tags进行编码额外信息的扩展。
GROM框架有自己的一个tags约定,如下所示:
Column 指定列名
Type 指定列数据类型
Size 指定列大小, 默认值255
PRIMARY_KEY 将列指定为主键
UNIQUE 将列指定为唯一
DEFAULT 指定列默认值
PRECISION 指定列精度
NOT NULL 将列指定为非 NULL
AUTO_INCREMENT 指定列是否为自增类型
INDEX 创建具有或不带名称的索引, 如果多个索引同名则创建复合索引
UNIQUE_INDEX 和 INDEX 类似,只不过创建的是唯一索引
EMBEDDED 将结构设置为嵌入
EMBEDDED_PREFIX 设置嵌入结构的前缀
- 忽略此字段
GROM还支持一些关联数据表的tags约定,有机会我讲讲GROM数据表关联的时候,会说到的。
上面列出的GORM支持的tags,方便我们定制结构体字段到数据表字段之间的映射规则,下面的代码,我们给Post结构体定制一些tags扩展,如下:
type Post struct {
PostId int `gorm:"primary_key;auto_increment"`
Uid int `gorm:"type:int;not null"`
Title string `gorm:"type:varchar(255);not null"`
Content string `gorm:"type:text;not null"`
Type uint8 `gorm:"type:tinyint;default 1;not null"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time
}
从上面的例子我们可以看出GORM为数据模型的字段定义tags的格式,每个字段可以用多个类型的tags信息,不同的tag之间用分号分隔。
惯例
除了上面讲的tags定义了字段之间的映射规则外,Go将结构体映射为关系型数据表时,还有自己的一套惯例,或称为约定,主要有以下几点:
主键
GROM的约定中,一般将数据模型中的ID字段映射为数据表的主键,如下面定义的TestModel,ID为主键,TestModel的ID的数据类型为string,如果ID的数据类型为int,则GROM还会为该设置AUTO_INCREMENT,使用ID成为自增主键。
type TestModel struct{
ID int
Name string
}
当然,我们也可以自定义主键字段的名称,如上面的Post结构体,我们设置了PostId字段为主键,如果我们定义了其他字段为主键,那么,就算结构体中仍有ID字段,GROM框架也不会把ID字段当作主键了。
type Post stru
QA"o^2cr84)A"fJ1V64)%A"f&7ZJ1bBcr4(#"ZbBcr7"f4)!4)"fV6_)5A4(_V6z,4)5A54(-^j[RP4)5A-IMQI%
PIMQI%
P4(j_*5A%4(j_*R5UU%4)4(4(>:rV6V6"o"f'N773B_rR/3:kN7r'^gGkV6#"[GrG>BV6jR/g^g=7z>CV6jRr |