# 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**
```
```