GORM模型(Model)創建


GORM模型(Model)創建

一、定義模型

type CreateUsers struct {
	gorm.Model
	Name         string
	Email        *string
	Age          uint8
	Birthday     *time.Time
	MemberNumber sql.NullString
	ActivatedAt  sql.NullTime
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

二、創建記錄

package main

import (
	"database/sql"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)



func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
    // CREATE TABLE `create_users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,`email` longtext,`a
ge` tinyint unsigned,`birthday` datetime(3) NULL,`member_number` longtext,`activated_at` datetime(3) NULL,PRIMARY KEY (`id`),INDEX idx_create_users_deleted_at (`deleted_at`))
	db.AutoMigrate(&CreateUsers{})

	// 創建數據
	timeNow := time.Now()
	user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}
	//INSERT INTO `create_users` (`created_at`,`updated_at`,`deleted_at`,`name`,`email`,`age`,`birthday`,`member_number`,`activated_at`) VALUES ('2021-12-01 22:04:30.799','2021-12-01 22:04:30.799',N
ULL,'RandySun',NULL,18,'2021-12-01 22:04:30.798',NULL,NULL)

	result := db.Debug().Create(&user) // 通過數據的指針來創建

	fmt.Println(user.ID)             // 返回插入數據的主鍵
	fmt.Println(result.Error)        // 返回 error
	fmt.Println(result.RowsAffected) // 返回插入記錄的條數

}

image-20211201220710597

image-20211201220722917

三、用指定的字段創建記錄

3.1 創建記錄並更新給出的字段。

package main

import (
	"database/sql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
	db.AutoMigrate(&CreateUsers{})

	// 創建數據
	timeNow := time.Now()
	user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}

	// 創建指定字段
	//  INSERT INTO `create_users` (`created_at`,`updated_at`,`name`,`age`) VALUES ('2021-12-01 22:08:47.894','2021-12-01 22:08:47.894','RandySun',18)
	db.Debug().Select("Name", "Age", "CreatedAt").Create(&user)
}

image-20211201221432255

image-20211201221531380

3.2 創建一個記錄且一同忽略傳遞給略去的字段值。

package main

import (
	"database/sql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
	db.AutoMigrate(&CreateUsers{})

	// 創建數據
	timeNow := time.Now()
	user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}

	// 插入排除字段
	//   INSERT INTO `create_users` (`updated_at`,`deleted_at`,`email`,`birthday`,`member_number`,`activated_at`) VALUES ('2021-12-01 22:17:54.491',NULL,NULL,'2021-12-01 22:17:54.491',NULL,NULL)
	db.Debug().Omit("Name", "Age", "CreatedAt").Create(&user)
}

image-20211201221834898

image-20211201221928581

四、批量插入

4.1 批量創建

要有效地插入大量記錄,請將一個 slice 傳遞給 Create 方法。 GORM 將生成單獨一條SQL語句來插入所有數據,並回填主鍵的值,鈎子方法也會被調用。

package main

import (
	"database/sql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
	db.AutoMigrate(&CreateUsers{})

	// 創建數據
	timeNow := time.Now()
	user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}

	// 批量插入數據
    // INSERT INTO `create_users` (`created_at`,`updated_at`,`deleted_at`,`name`,`email`,`age`,`birthday`,`member_number`,`activated_at`) VALUES ('2021-12-01 22:22:37.647','2021-12-01 22:22:37.647',N
ULL,'RandySun01',NULL,0,NULL,NULL,NULL),('2021-12-01 22:22:37.647','2021-12-01 22:22:37.647',NULL,'RandySun02',NULL,0,NULL,NULL,NULL),('2021-12-01 22:22:37.647','2021-12-01 22:22:37.647',NULL,'RandySun03',NULL,0,NULL,NULL,NULL)

	var users = []CreateUsers{{Name: "RandySun01"}, {Name: "RandySun02"}, {Name: "RandySun03"}}
	db.Debug().Create(&users)
	for _, user := range users {
		fmt.Println(user.ID) // 4,5,6
	}
}

image-20211201222410743

image-20211201222401759

4.2 分批創建

使用 CreateInBatches 分批創建時,你可以指定每批的數量,例如:

package main

import (
	"database/sql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
	db.AutoMigrate(&CreateUsers{})

	// 創建數據
	timeNow := time.Now()
	user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}

	// 分批創建
	var users = []CreateUsers{{Name: "RandySun01"}, {Name: "RandySun02"},{Name: "RandySun04"},{Name: "RandySun0...."}, {Name: "RandySun200000"}}

	// 數量為 2
	db.Debug().CreateInBatches(users, 2)

	for _, user := range users {
		fmt.Println(user.ID) // 4,5,6
	}
}


image-20211201222904657

image-20211201222929455

UpsertCreate With Associations 也支持批量插入

注意 使用CreateBatchSize 選項初始化 GORM 時,所有的創建& 關聯 INSERT 都將遵循該選項

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  CreateBatchSize: 1000,
})

db := db.Session(&gorm.Session{CreateBatchSize: 1000})

users = [5000]User{{Name: "jinzhu", Pets: []Pet{pet1, pet2, pet3}}...}

db.Create(&users)
// INSERT INTO users xxx (5 batches)
// INSERT INTO pets xxx (15 batches)

五、創建鈎子

GORM 允許用戶定義的鈎子有 BeforeSave, BeforeCreate, AfterSave, AfterCreate 創建記錄時將調用這些鈎子方法,請參考 Hooks 中關於生命周期的詳細信息

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  u.UUID = uuid.New()

    if u.Role == "admin" {
        return errors.New("invalid role")
    }
    return
}
package main

import (
	"database/sql"
	"errors"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

type CreateUsers struct {
	gorm.Model
	Name         string
	Email        *string
	Age          uint8
	Birthday     *time.Time
	MemberNumber sql.NullString
	ActivatedAt  sql.NullTime
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

func (u *CreateUsers) BeforeCreate(tx *gorm.DB) (err error) {
	fmt.Println("創建之前觸發鈎子")
	if u.Name == "RandySun" {
		return errors.New("invalid role")
	}
	return
}
func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
	db.AutoMigrate(&CreateUsers{})

	// 創建數據
	timeNow := time.Now()
	user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}

	// 創建用戶
	result := db.Debug().Create(&user) // 通過數據的指針來創建

	fmt.Println(user.ID)             // 返回插入數據的主鍵
	fmt.Println(result.Error)        // 返回 error
	fmt.Println(result.RowsAffected) // 返回插入記錄的條數

}

image-20211201223519120

如果您想跳過 鈎子 方法,您可以使用 SkipHooks 會話模式,例如:

DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)

DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)

DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)

六、根據 Map 創建

GORM 支持根據 map[string]interface{}[]map[string]interface{}{} 創建記錄,例如:

package main

import (
	"database/sql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
	db.AutoMigrate(&CreateUsers{})

	// 根據 Map 創建

	db.Debug().Model(&CreateUsers{}).Create(map[string]interface{}{
		"Name": "RandySun", "Age": 18,
	})

	// batch insert from `[]map[string]interface{}{}`
	db.Debug().Model(&CreateUsers{}).Create([]map[string]interface{}{
		{"Name": "RandySunMap01", "Age": 18},
		{"Name": "RandySunMap02", "Age": 20},
	})
	
}


注意: 根據 map 創建記錄時,association 不會被調用,且主鍵也不會自動填充

image-20211201224409221

image-20211201224518631

七、使用 SQL 表達式、Context Valuer 創建記錄

GORM 允許使用 SQL 表達式插入數據,有兩種方法實現這個目標。根據 map[string]interface{}自定義數據類型 創建,例如:

// 通過 map 創建記錄
db.Model(User{}).Create(map[string]interface{}{
  "Name": "jinzhu",
  "Location": clause.Expr{SQL: "ST_PointFromText(?)", Vars: []interface{}{"POINT(100 100)"}},
})
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"));

// 通過自定義類型創建記錄
type Location struct {
    X, Y int
}

// Scan 方法實現了 sql.Scanner 接口
func (loc *Location) Scan(v interface{}) error {
  // Scan a value into struct from database driver
}

func (loc Location) GormDataType() string {
  return "geometry"
}

func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
  return clause.Expr{
    SQL:  "ST_PointFromText(?)",
    Vars: []interface{}{fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y)},
  }
}

type User struct {
  Name     string
  Location Location
}

db.Create(&User{
  Name:     "jinzhu",
  Location: Location{X: 100, Y: 100},
})
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))

八、高級選項

關聯創建

創建關聯數據時,如果關聯值是非零值,這些關聯會被 upsert,且它們的 Hook 方法也會被調用

package main

import (
	"database/sql"
	"errors"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

type CreditCard struct {
	gorm.Model
	Number string
	UserID uint
}
type CreateUsers struct {
	gorm.Model
	Name         string
	Email        *string
	Age          uint8
	Birthday     *time.Time
	MemberNumber sql.NullString
	ActivatedAt  sql.NullTime
	CreatedAt    time.Time
	UpdatedAt    time.Time
	CreditCard   CreditCard `gorm:"foreignKey:ID"`
}

func (u *CreateUsers) BeforeCreate(tx *gorm.DB) (err error) {
	fmt.Println("創建之前觸發鈎子")
	if u.Name == "RandySun" {
		return errors.New("invalid role")
	}
	return
}
func main() {
	dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 遷移表創建對應關系
	db.AutoMigrate(&CreateUsers{}, &CreditCard{})

	// 關聯創建

	db.Debug().Create(&CreateUsers{
		Name:       "Randy",
		CreditCard: CreditCard{Number: "34353435"},
	})
}

image-20211201230607234

image-20211201230638413

您也可以通過 SelectOmit 跳過關聯保存,例如:

db.Omit("CreditCard").Create(&user)

// 跳過所有關聯
db.Omit(clause.Associations).Create(&user)

九、默認值

您可以通過標簽 default 為字段定義默認值,如:

type User struct {
  ID   int64
  Name string `gorm:"default:galeone"`
  Age  int64  `gorm:"default:18"`
}

插入記錄到數據庫時,默認值 會被用於 填充值為 零值 的字段

注意0''false 等零值,不會將這些字段定義的默認值保存到數據庫。您需要使用指針類型或 Scanner/Valuer 來避免這個問題,例如:

type User struct {
  gorm.Model
  Name string
  Age  *int           `gorm:"default:18"`
  Active sql.NullBool `gorm:"default:true"`
}

注意 若要數據庫有默認、虛擬/生成的值,你必須為字段設置 default 標簽。若要在遷移時跳過默認值定義,你可以使用 default:(-),例如:

type User struct {
  ID        string `gorm:"default:uuid_generate_v3()"` // db func
  FirstName string
  LastName  string
  Age       uint8
  FullName  string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
}

使用虛擬/生成的值時,你可能需要禁用它的創建、更新權限,查看 字段級權限 獲取詳情

九、Upsert 及沖突

GORM 為不同數據庫提供了兼容的 Upsert 支持

import "gorm.io/gorm/clause"

// 在沖突時,什么都不做
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user)

// 在`id`沖突時,將列更新為默認值
db.Clauses(clause.OnConflict{
  Columns:   []clause.Column{{Name: "id"}},
  DoUpdates: clause.Assignments(map[string]interface{}{"role": "user"}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET ***; SQL Server
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE ***; MySQL

// 使用SQL語句
db.Clauses(clause.OnConflict{
  Columns:   []clause.Column{{Name: "id"}},
  DoUpdates: clause.Assignments(map[string]interface{}{"count": gorm.Expr("GREATEST(count, VALUES(count))")}),
}).Create(&users)
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `count`=GREATEST(count, VALUES(count));

// 在`id`沖突時,將列更新為新值
db.Clauses(clause.OnConflict{
  Columns:   []clause.Column{{Name: "id"}},
  DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age=VALUES(age); MySQL

// 在沖突時,更新除主鍵以外的所有列到新值。
db.Clauses(clause.OnConflict{
  UpdateAll: true,
}).Create(&users)
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age", ...;

您還可以查看 高級查詢 中的 FirstOrInitFirstOrCreate

查看 原生 SQL 及構造器 獲取更多細節


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM