# 05. Gorm Framwork V2(Demo by MySQL) ###### tags: `golang練習` ## TL;DR: 1. DB不要開起來等 2. 若一開始沒資料, Migrate時, FK constraint會錯誤, 需手動於&gorm.Config設置關閉 3. 不要輕易做Migration的動作,需有資料庫人員認可或直接給他們操作(大公司) 4. MySQL的DB連線不一定要做關閉動作,但Redis一定要 5. DB等連線都可以使用pool開 ## DB ```go // 資料庫連接資料 type Db_message struct { Username string Password string Addr string Database string Max_lifetime int Max_openconns int Max_idleconns int } func Db_init(config *tools.Config_data, fk_constraint bool) (db_conn *gorm.DB, err error) { // gorm資料庫連接 dsn := fmt.Sprintf("%s:%s@tcp(%s)/?charset=utf8mb4&parseTime=True&loc=Local", config.Mysql.Username, config.Mysql.Password, config.Mysql.Addr) db_conn, err = gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}) if err != nil { return nil, err } // 若無則新建資料庫(schema) err = db_conn.Exec("CREATE DATABASE IF NOT EXISTS " + config.Mysql.Database).Error if err != nil { return nil, err } // 重新連接指定schema dsn = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Mysql.Username, config.Mysql.Password, config.Mysql.Addr, config.Mysql.Database) db_conn, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), DisableForeignKeyConstraintWhenMigrating: !fk_constraint, }) if err != nil { return nil, err } // mysql資料庫本體 db, err := db_conn.DB() if err != nil { return nil, err } // 確保資料庫安全關閉 db.SetConnMaxLifetime(time.Duration(config.Mysql.MaxLifetime) * time.Second) // 閒置連接數(官方建議跟SetMaxOpenConns一致) db.SetMaxIdleConns(config.Mysql.MaxIdleconns) // 限制資料庫連接數 db.SetMaxOpenConns(config.Mysql.MaxOpenconns) return db_conn, nil } func Db_migrate(db_conn *gorm.DB) error { all_tables := []interface{}{&Account{}, &Todolist{}} // 更新資料庫資料 err := db_conn.AutoMigrate(all_tables...) if err != nil { return err } // 確認table存在 migrator := db_conn.Migrator() for _, v := range all_tables { has := migrator.HasTable(v) if !has { return err } } // 關閉DB err = Db_close(db_conn) if err != nil { return err } return nil } func Db_conn_web(c *gin.Context) (*gorm.DB, error) { configs_raw, b := c.Get("configs") if !b { err := errors.New("can't get configs") return nil, err } configs := configs_raw.(*tools.Config_data) db_conn, err := Db_init(configs, false) if err != nil { return nil, err } return db_conn, nil } func Db_close(db_conn *gorm.DB) error { db, err := db_conn.DB() if err != nil { return err } err = db.Close() if err != nil { return err } return nil } ``` --- ## DB tables ```go /// Accounts table type Account struct { gorm.Model Username string `gorm:"type:varchar(30) NOT NULL;"` Password string `gorm:"type:varchar(64) NOT NULL;"` Todolists []Todolist `gorm:"foreignKey:AccountID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` } // Todolist table type Todolist struct { gorm.Model Subject string `gorm:"type:varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;"` Status int `gorm:"type:int NOT NULL;"` AccountID uint } ``` 1. **FK tags** >On Delete和On Update都有Restrict, No Action, Cascade, Set Null屬性。 > >ON DELETE: restrict(約束):當在父表(即外來鍵的來源表)中刪除對應記錄時,首先檢查該記錄是否有對應外來鍵,如果有則不允許刪除。 no action:意思同restrict.即如果存在從資料,不允許刪除主資料。 cascade(級聯):當在父表(即外來鍵的來源表)中刪除對應記錄時,首先檢查該記錄是否有對應外來鍵,如果有則也刪除外來鍵在子表(即包含外來鍵的表)中的記錄。 set null:當在父表(即外來鍵的來源表)中刪除對應記錄時,首先檢查該記錄是否有對應外來鍵,如果有則設定子表中該外來鍵值為null(不過這就要求該外來鍵允許取null) > >ON UPDATE: restrict(約束):當在父表(即外來鍵的來源表)中更新對應記錄時,首先檢查該記錄是否有對應外來鍵,如果有則不允許更新。 no action:意思同restrict. cascade(級聯):當在父表(即外來鍵的來源表)中更新對應記錄時,首先檢查該記錄是否有對應外來鍵,如果有則也更新外來鍵在子表(即包含外來鍵的表)中的記錄。 set null:當在父表(即外來鍵的來源表)中更新對應記錄時,首先檢查該記錄是否有對應外來鍵,如果有則設定子表中該外來鍵值為null(不過這就要求該外來鍵允許取null)。 > >注:NO ACTION和RESTRICT的區別:只有在及個別的情況下會導致區別,前者是在其他約束的動作之後執行,後者具有最高的優先權執行。 2. **This FK is one to many type** ``` ```