上一节我们介绍了gorm的连接和配置,并且我们也在数据库中创建了 admins、article_data、articles、attachments、categories 五个表。但是我们还没有将它和golang结合在一起,我们的博客网站还不能从数据库读写数据。需要使用gorm来读写数据,我们还需要先给每个表定义一个模型(struct)。
前面建立目录的时候,我们已经定义了数据库模型文件存放的目录 为model
文件夹。因此我们在model目录分别按表名称的单数形式创建对应的go文件,并定义对应的模型结构体。
admin.go 文件的 admins 表
type Admin struct {
Id uint `json:"id" gorm:"column:id;type:int(10) unsigned not null AUTO_INCREMENT;primaryKey;"`
UserName string `json:"user_name" gorm:"column:user_name;type:varchar(16) not null;default:'';index:idx_user_name"`
Password string `json:"-" gorm:"column:password;type:varchar(128) not null;default:''"`
Status uint `json:"status" gorm:"column:status;type:tinyint(1) unsigned not null;default:0;index:idx_status"`
CreatedTime int64 `json:"created_time" gorm:"column:created_time;type:int(11) not null;default:0;index:idx_created_time"`
UpdatedTime int64 `json:"updated_time" gorm:"column:updated_time;type:int(11) not null;default:0;index:idx_updated_time"`
DeletedTime int64 `json:"-" gorm:"column:deleted_time;type:int(11) not null;default:0"`
}
表名称的结构体是表名的单数形式,因为默认gorm在读取表的时候,会使用复数结构体名称的复数形式作为表名。当然,我们也可以通过自定义表名的形式,来重新定义表名,不使用gorm的默认表名:
func (Admin) TableName() string {
return "admins"
}
自定义表名使用 TableName() 函数来重写。
同时要注意,结构体名称一定要使用驼峰命名法则来命名,单词首字母大写。结构体内部字段也是如此,必须要使用驼峰命名法则来命名,首字母大写,外部才能访问到这个字段,表字段和json字段则使用蛇形小写的命名方式,即它们的两个单词之间使用下划线_
来连接。后面的结构体字段标记则是golang在解析这个字段的时候的变量,比如这些字段都应以了作为json输出的时候的字段名称。如果这个字段希望在转换为json的时候,忽略它,则将它的json标记为-
即可,如:
Password string `json:"-"`
后面的gorm标记,则是gorm对这个字段的数据库定义信息,它包含了这个字段在表中的字段名、字段类型、字段长度、无符号值、字段自增、关键字段、默认值、索引信息等内容,如果有必要,gorm会根据这些内容来创建和更新mysql表信息。
同样地,如果希望这个结构体中,某个字段不出现在数据库中,同样可以使用-
来忽略它,如:
Content int64 `json:"content" gorm:"-"`
这样这个字段可以通过外部赋值,并且在转换成json输出的时候,它是有值的,而它不会被写入到数据库中,gorm使用orm查查询数据库的时候,它也不会被查询出来。
category.go 文件的 categories 表
type Category struct {
Id uint `json:"id" gorm:"column:id;type:int(10) unsigned not null AUTO_INCREMENT;primary_key"`
Title string `json:"title" gorm:"column:title;type:varchar(250) not null;default:''"`
Description string `json:"description" gorm:"column:description;type:varchar(250) not null;default:''"`
Content string `json:"content" gorm:"column:content;type:longtext default null"`
ParentId uint `json:"parent_id" gorm:"column:parent_id;type:int(10) unsigned not null;default:0;index:idx_parent_id"`
Status uint `json:"status" gorm:"column:status;type:tinyint(1) unsigned not null;default:0;index:idx_status"`
CreatedTime int64 `json:"created_time" gorm:"column:created_time;type:int(11) not null;default:0;index:idx_created_time"`
UpdatedTime int64 `json:"updated_time" gorm:"column:updated_time;type:int(11) not null;default:0;index:idx_updated_time"`
DeletedTime int64 `json:"-" gorm:"column:deleted_time;type:int(11) not null;default:0"`
}
模型字段基本上就是根据mysql表的字段一一对应的,并且将字段的信息都标记在结构体字段的标记中。
gorm的标记也可以省略,省略的话,它默认就是模型结构自字段的蛇形小写形式,如:
type Category struct {
Id uint //默认字段名是id
Title string //默认字段名是title
Description string //默认字段名是description
Content string //默认字段名是content
ParentId uint //默认字段名是parent_id
Status uint //默认字段名是status
CreatedTime int64 //默认字段名是created_time
UpdatedTime int64 //默认字段名是updated_time
DeletedTime int64 //默认字段名是deleted_time
}
为了方便gorm可以同步迁移数据库,建议我们采用完整的写法。这样我们就可以在有更新字段的时候,使用 AutoMigrate 函数来完成数据迁移工作,对部署很有帮助。
注意:自动迁移仅仅会创建表,缺少列和索引,并且不会改变现有列的类型或删除未使用的列以保护数据。
article.go 文件的 articles 表 和 article_data 表
type Article struct {
Id uint `json:"id" gorm:"column:id;type:int(10) unsigned not null AUTO_INCREMENT;primary_key"`
Title string `json:"title" gorm:"column:title;type:varchar(250) not null;default:''"`
Keywords string `json:"keywords" gorm:"column:keywords;type:varchar(250) not null;default:''"`
Description string `json:"description" gorm:"column:description;type:varchar(250) not null;default:''"`
CategoryId uint `json:"category_id" gorm:"column:category_id;type:int(10) unsigned not null;default:0;index:idx_category_id"`
Views uint `json:"views" gorm:"column:views;type:int(10) unsigned not null;default:0;index:idx_views"`
Status uint `json:"status" gorm:"column:status;type:tinyint(1) unsigned not null;default:0;index:idx_status"`
CreatedTime int64 `json:"created_time" gorm:"column:created_time;type:int(11) not null;default:0;index:idx_created_time"`
UpdatedTime int64 `json:"updated_time" gorm:"column:updated_time;type:int(11) not null;default:0;index:idx_updated_time"`
DeletedTime int64 `json:"-" gorm:"column:deleted_time;type:int(11) not null;default:0"`
Category Category `json:"category" gorm:"-"`
ArticleData ArticleData `json:"data"`
}
type ArticleData struct {
ArticleId uint `json:"article_id" gorm:"column:article_id;type:int(10) unsigned not null;primary_key"`
Content string `json:"content" gorm:"column:content;type:longtext default null"`
}
因为articles 表和 article_data 表它们是紧密联系在一起的,因此我们将这两个模型结构体创建在了同一个文件中,方便我们查看和管理。
这里说明一下,Id 字段为自增字段,CategoryId 字段、Views 字段、Status 字段 它们的值都是只有大于零的值,因此我们定义它们的类型为uint;而CreatedTime、UpdatedTime、DeletedTime字段为时间戳字段,golang默认的时间戳类型是int64,为了减少不必要的类型转换,因此我们定义模型结构体的时候,也将它们定义为int64类型,这样不需要再做类型转换就可以直接调用这个值。
这两个模型结构体里,我们定义了Article 为 ArticleData 的外键,并且它们就通过外键将两个表绑定在一起了,可以做到数据的完整性。Category 分类表因为不是一对一关系,所以这里我们在gorm中就做忽略处理。
attachment.go 文件的 attachments 表
type Attachment struct {
Id uint `json:"id" gorm:"column:id;type:int(10) unsigned not null AUTO_INCREMENT;primaryKey;"`
FileName string `json:"file_name" gorm:"column:file_name;type:varchar(100) not null;default:''"`
FileLocation string `json:"file_location" gorm:"column:file_location;type:varchar(250) not null;default:''"`
FileSize int64 `json:"file_size" gorm:"column:file_size;type:bigint(20) unsigned not null;default:0"`
FileMd5 string `json:"file_md5" gorm:"column:file_md5;type:varchar(32) unique not null;default:''"`
Width uint `json:"width" gorm:"column:width;type:int(10) unsigned not null;default:0"`
Height uint `json:"height" gorm:"column:height;type:int(10) unsigned not null;default:0"`
Status uint `json:"status" gorm:"column:status;type:tinyint(1) unsigned not null;default:0;index:idx_status"`
CreatedTime int64 `json:"created_time" gorm:"column:created_time;type:int(11) not null;default:0;index:idx_created_time"`
UpdatedTime int64 `json:"updated_time" gorm:"column:updated_time;type:int(11) not null;default:0;index:idx_updated_time"`
DeletedTime int64 `json:"-" gorm:"column:deleted_time;type:int(11) not null;default:0"`
Logo string `json:"logo" gorm:"-"`
Thumb string `json:"thumb" gorm:"-"`
}
Attachment模型结构体,我们定义了Logo和Thumb两个不在mysql表中存在的字段,因为 FileLocation 字段存放的是上传的图片的服务器存储路径,并不一定是浏览器可以直接浏览的文件路径,因为我们在图片展示的时候,还需要将它转换成可浏览器浏览的路径,图片还有原图和缩略图的区分,所以我们定义了Logo字段为图片原图可访问路径,Thumb 为图片缩略图可访问路径。这两个自动会在gorm读出数据后,再通过其他方法来给它赋值。
上面就是我们博客需要用到的5个表的gorm模型的定义。模型定义完了之后,我们就可以对数据进行操作了。下一节我们将介绍如何操作数据库。
完整的项目示例代码托管在GitHub上,需要查看完整的项目代码可以到github.com/fesiong/goblog 上查看,也可以直接fork一份来在上面做修改。