原创做者,公众号【程序员读书】,欢迎关注公众号,转载文章请注明出处哦。程序员
在开发应用程序时,通常而言,咱们是先设计好数据表,再使用开发语言创建对应的数据模型,不过,咱们今天要讲的是一个逆向操做的过程,即如何通定义GORM
框架的数据模型,而后再经过执行GROM
框架编写的应用程序,用定义好数据模型在数据库中建立对应的数据表。sql
所以须要先讲讲怎么定义GORM
的数据模型。数据库
通常来讲,咱们说GROM
的模型定义,是指定义表明一个数据表的结构体(struct
),而后咱们可使用GROM
框架能够将结构体映射为相对应的关系数据库的数据表,或者查询数据表中的数据来填充结构体,以下所示,咱们定义了一个名为Post
的结构体。json
type Post struct { PostId int Uid int Title string Content string Type int CreatedAt time.Time UpdatedAt time.Time } 复制代码
建立好一个结构体只是第一步,不过先不着急要怎么去建立数据表,咱们要先了解一下结构体与数据表之间的映射规则,主要有如下几点:bash
咱们知道,Go
语言的结构体支持使用tags
为结构体的每一个字段扩展额外的信息,如使用标准库encoding/json
包进行JSON
编码时,即可以使用tags
进行编码额外信息的扩展。markdown
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数据表关联的时候,会说到的。post
上面列出的GORM支持的tags,方便咱们定制结构体字段到数据表字段之间的映射规则,下面的代码,咱们给Post
结构体定制一些tags
扩展,以下:ui
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 struct { ID int 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 } 复制代码
因此,咱们在Post
结构体中加一个ID
字段,PostId
字段还是主键,下面是在数据中使用desc posts
语句打印出来的结果:
当咱们使用结构体建立数据表时,数据表的名称默认为结构体的小写复数形式,如结构体Post
对应的数据表名称为posts
,固然咱们也能够本身指定结构体对应的数据表名称,而不是用默认的。
为结构体加上TableName()
方法,经过这个方法能够返回自定义的数据表名,以下:
//指定Post结构体对应的数据表为my_posts func (p Post) TableName() string{ return "my_posts" } 复制代码
除了指定数据表名外,咱们也能够重写gorm.DefaultTableNameHandler
这个变量,这样能够为全部数据表指定统一的数据表前缀,以下:
gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string { return "tb_" + defaultTableName; } 复制代码
这样的话,经过结构体Post
建立的数据表名称则为tb_posts
。
结构体到数据表的名称映射规则为结构体名称的复数,而结构体的字段到数据表字段的默认映射规则是用下划线分隔每一个大写字母开头的单词,以下:
type Prize struct { ID int PrizeName string } 复制代码
上面的结构体Prize
中的PrizeName
字段对应的数据表为prize_name
,但咱们把PrizeName
改成Prizename
时,则对应的数据表字段名称为prizename
,这是为由于只分隔大写字段开头的单词。
固然,咱们也能够为结构体的某个字段定义tags
扩展信息,这样结构体字段到数据表字段的映规则就在tags
中定义。
前面咱们说过,若是结构体中有名称为ID
字段,则GORM
框架会把该字段做为数据表的主键,除此以外,若是结构体中有CreatedAt
,UpdatedAt
,DeletedAt
这几个字段的话,则GROM
框架也会做一些特殊处理,规则以下:
CreatedAt:新增数据表记录的时候,会自动写入这个字段。
UpdatedAt:更新数据表记录的时候,会自动更新这个字段。
DeletedAt:当执行软删除的时候,会自动更新这个字段,表示删除时间
复制代码
因为若是结构体中有ID
,CreatedAt
,UpdatedAt
,DeletedAt
这几个比较通用的字段,GORM
框架会自动处理这几个字段,因此若是咱们结构体须要这几个字段时,咱们能够直接在自定义结构体中嵌入gorm.Model
结构体,gorm.Model
的结构体以下:
type Model struct { ID uint `gorm:"primary_key"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time `sql:"index"` } 复制代码
因此,若是咱们在结构体Prize
中嵌入gorm.Model
,以下:
type Prize struct{ gorm.Model Name string } 复制代码
这样的话,则结构体Prize
包含有五个字段了。
咱们这里所说的数据库迁移,即经过使用GROM
提供的一系列方法,根据数据模型定义好的规则,进行建立、删除数据表等操做,也就是数据库的DDL操做。
GORM
提供对数据库进行DDL操做的方法,主要如下几类:
//根据模型自动建立数据表 func (s *DB) AutoMigrate(values ...interface{}) *DB //根据模型建立数据表 func (s *DB) CreateTable(models ...interface{}) *DB //删除数据表,至关于drop table语句 func (s *DB) DropTable(values ...interface{}) *DB //至关于drop table if exsist 语句 func (s *DB) DropTableIfExists(values ...interface{}) *DB //根据模型判断数据表是否存在 func (s *DB) HasTable(value interface{}) bool 复制代码
//删除数据表字段
func (s *DB) DropColumn(column string) *DB
//修改数据表字段的数据类型
func (s *DB) ModifyColumn(column string, typ string) *DB
复制代码
//添加外键
func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate string) *DB
//给数据表字段添加索引
func (s *DB) AddIndex(indexName string, columns ...string) *DB
//给数据表字段添加惟一索引
func (s *DB) AddUniqueIndex(indexName string, columns ...string) *DB
复制代码
数据迁移简单代码示例
注意,下面示例程序中
db
变量表明gorm.DB
对象,其初始化过程能够参考个人上一篇文章。
type User struct { Id int //对应数据表的自增id Username string Password string Email string Phone string } func main(){ db.AutoMigrate(&Post{},&User{})//建立posts和users数据表 db.CreateTable(&Post{})//建立posts数据表 db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&Post{})//建立posts表时指存在引擎 db.DropTable(&Post{},"users")//删除posts和users表数据表 db.DropTableIfExists(&Post{},"users")//删除前会判断posts和users表是否存在 //先判断users表是否存在,再删除users表 if db.HasTable("users") { db.DropTable("users") } //删除数据表字段 db.Model(&Post{}).DropColumn("id") //修改字段数据类型 db.Model(&Post{}).ModifyColumn("id","varchar(255)") //创建posts与users表之间的外键关联 db.Model(&Post{}).AddForeignKey("uid", "users(id)", "RESTRICT", "RESTRICT") //给posts表的title字段添加索引 db.Model(&Post{}).AddIndex("index_title","title") //给users表的phone字段添加惟一索引 db.Model(&User{}).AddUniqueIndex("index_phone","phone") } 复制代码
可能你会问,直接在数据库中进行数据表建立、删除等操做不就好了吗?为何要在应用程序里去作这些操做呢?由于有些时候,咱们不必定能登陆到数据库系统当中,又或者,咱们须要开发一个能够管理数据库的应用程序,这时候,GROM
框架提供的这些数据库迁移的能便派上用场了。
你的关注,是我写做路上最大的鼓励!