文章首发于公众号【程序员读书】,欢迎关注。
这是《Golang GORM实战》系列的第三篇,在这篇文章中我们来聊一聊GORM数据模型的一些细节。
模型定义
GORM的数据模型是一个标准的Go struct,一个数据模型代表一张数据表,模型由基本数据类型和实现了Scanner和Valuer接口的自定义类型及其指针或者别名组成,如:
typeUserstruct{IDuintNamestringEmail*stringAgeuint8Birthday*time.TimeMemberNumbersql.NullStringActivatedAtsql.NullTimeCreatedAttime.TimeUpdatedAttime.Time}
gorm.Model
对于一张数据表的每一条数据来说,都有自增主键,创建、更新与删除时间等比较通用的字段,因此GORM将其抽出来并定义了gorm.Model,如:
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}
如果有需要,我们可以将gorm.Model嵌入到我们的模型中,而不用重复定义那几个字段,如:
typeUserstruct{gorm.ModelNamestring}
上面嵌入gorm.Model的User等同于下面的User:
typeUserstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`Namestring}
嵌入结构体
上面我们已经在自己定义的结构体中嵌入gorm.Model,当然,我们也可以嵌入自定义的结构,匿名嵌入,这种方式与上面嵌入gorm.Model相同,如:
typeUserstruct{UsernamestringAgeint}typeHousestruct{IDintNamestringUser}//等同于typeHousestruct{IDintNamestringUsernamestringAgeint}
而非匿名嵌入,也就是正常的结构体字段,则需要声明字段标签embedded
才可以嵌入,否则会被GORM当作关联模型处理,但于未定义关联关系,所以会报错,如:
//错误typeHousestruct{IDintNamestringUserUser}//正确typeHousestruct{IDintNamestringUserUser`gorm:"embedded"`}
另外,无论是正常结构体嵌入还是匿名嵌入,都可以使用字段标签embeddedPrefix
来指定嵌入结构全部字段的前缀,如:
typeHousestruct{IDintNamestringUserUser`gorm:"embedded;embeddedPrefix:house_"`}//等同于typeHousestruct{IDintNamestringHouseUsernamestringHouserAgeint}
主键ID
GORM默认会把ID作为数据表的主键,如:
typeUserstruct{IDstring//默认情况下,名为`ID`的字段会作为表的主键Namestring}
如果ID是整型且为自增字段的话,则在新增时,会自动填充该值,如:
//ID此时为0varu=&User{Name:"小张"}//创建成功后,此时ID自动填充为数据表自增字段的值db.Create(u)
如果想设置其他字段为主键的话,可以通过字段标签primaryKey
来指定,如:
typeUserstruct{UserIDstring`gorm:"primaryKey"`Namestring}
另外,也可以将多个字段设置为字段,而多个字段组成的主键也叫复合主键,如:
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}0
无论是ID,还是通过primaryKey设置的单字段主键或联合主键,如果字段的数据类型为整型,GORM都会把该字段设置为自增字段(AUTO_INCREMENT),可以通过autoIncrement
设置为false来禁用这个功能,如:
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}1
时间追踪
GORM约定使用CreatedAt,Updatedat,DeletedAt追踪记录创建,更新,软删除的时间,如果在数据模型中定义了这几个字段,则在创建、更新、软删除记录时,会自动填充时间。
CreatedAt
如果数据模型有CreatedAt字段时,在创建记录时,如果没有指CreatedAt的值,则会将字段的值设置为当前时间。
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}2
也可以自己指定
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}3
也可以手动修改
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}4
UpdatedAt
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}5
DeletedAt
DeletedAt字段在使用Delete方法
删除数据表记录会起作用,具体我们在系列的其他文章讲解到删除记录会涉及到。
覆盖表名
GORM使用结构体的蛇形命名
作为数据表名称,如,对于User结构体来说,其表名为users,而GameUser结构的表名为game_users。
不过,如果不想按GORM的规则来指定数据表名,我们可以让数据模型实现Tabler接口,该接口的定义如下:
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}6
让User结构体实现Tabler接口,如:
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}7
当然,除了定义模型的TableName方法外,也可以使用Scope动态生成表名,或者在执行GORM的Create, First, Find, Take, Save, Update, Delete等方法时,临时指定数据表名,当然如果你想指定模型的表名的话,定义模型的TableName方法是最简单,因此推荐通过这种方法来指定表名。
覆盖列名
数据表的列名使用的也是数据模型的蛇形命名
,如:
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}8
不过,可以使用column标签来覆盖列表,如:
//gorm.Model的定义typeModelstruct{IDuint`gorm:"primaryKey"`CreatedAttime.TimeUpdatedAttime.TimeDeletedAtgorm.DeletedAt`gorm:"index"`}9
标签(tag)
字段标签
对于数据模型(model)来说,字段标签并不是必须的,而是可选的,tag大小写不敏感,如primarykey
和primaryKey
是一样的,不过还是推荐按下表列出的名称去使用。
上述的字段标签有很大一部部分是用于迁移数据表的,不过这里不推荐使用GORM的数据迁移功能,这是因为对数据表的创建、删除、修改都是比较重大的变更和高危操作,因此需要严格评估后再实施操作,而不应该写在程序代码里。
而对于上述的标签的说明,有个大概印象就好,如须使用,再查看文档即可。
关联标签
另外,下面列出来的字段标签与数据模型的关联有关,我们在之后的文章再行讲解。
小结
对于GORM来说,数据模型其实是就是Go struct,对应一个数据库的数据表,GORM约定了许多数据模型到数据表的映射规则,比如表名与列名的蛇形复数命命规则,默认ID字段为主键等,但我们仍然可以通过对数据模型方法和标签的定义来改变这些约定,比如通过TableName方法来改变模型对应的数据表等,对GORM数据模型的理解,可以让我们更好地去使用GORM操作数据表。