原創做者,公衆號【程序員讀書】,歡迎關注公衆號,轉載文章請註明出處哦。程序員
在開發應用程序時,通常而言,咱們是先設計好數據表,再使用開發語言創建對應的數據模型,不過,咱們今天要講的是一個逆向操做的過程,即如何通定義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
框架提供的這些數據庫遷移的能便派上用場了。
你的關注,是我寫做路上最大的鼓勵!