Golang數據庫編程之GORM模型定義與數據庫遷移

原創做者,公衆號【程序員讀書】,歡迎關注公衆號,轉載文章請註明出處哦。程序員

在開發應用程序時,通常而言,咱們是先設計好數據表,再使用開發語言創建對應的數據模型,不過,咱們今天要講的是一個逆向操做的過程,即如何通定義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

Struct tags

咱們知道,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爲主鍵,TestModelID的數據類型爲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:當執行軟刪除的時候,會自動更新這個字段,表示刪除時間
複製代碼
gorm.Model

因爲若是結構體中有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框架提供的這些數據庫遷移的能便派上用場了。


你的關注,是我寫做路上最大的鼓勵!

相關文章
相關標籤/搜索