创建

创建记录

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

result := db.Create(&user) // pass pointer of data to Create

user.ID // returns inserted data's primary key
result.Error // returns error
result.RowsAffected // returns inserted records count

我们还可以使用 Create() 创建多个记录

users := []*User{
{Name: "Jinzhu", Age: 18, Birthday: time.Now()},
{Name: "Jackson", Age: 19, Birthday: time.Now()},
}

result := db.Create(users) // pass a slice to insert multiple row

result.Error // returns error
result.RowsAffected // returns inserted records count

注意 你不能将结构传递给“创建”,因此你应该传递指向数据的指针。

创建具有所选字段的记录

创建一个记录,并为指定的字段分配一个值。

db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")

创建一个记录,并忽略传递给忽略项的值。

db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")

批量插入

为了有效地插入大量记录,将切片传递给 Create 方法。GORM 将生成一个 SQL 语句来插入所有数据并回填主键值,也会调用钩子方法。当记录可以分成多个批次时,它将开始一个事务

var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)

for _, user := range users {
user.ID // 1,2,3
}

你可以在使用 CreateInBatches 创建时指定批次大小,例如

var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}

// batch size 100
db.CreateInBatches(users, 100)

在使用 Upsert创建关联 时,也支持批量插入

注意 使用 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 允许为 BeforeSaveBeforeCreateAfterSaveAfterCreate 实现用户定义的钩子。在创建记录时将调用这些钩子方法,有关生命周期的详细信息,请参阅 钩子

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

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

如果你想跳过 Hooks 方法,可以使用 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)

从映射创建

GORM 支持从 map[string]interface{}[]map[string]interface{}{} 创建,例如

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

// batch insert from `[]map[string]interface{}{}`
db.Model(&User{}).Create([]map[string]interface{}{
{"Name": "jinzhu_1", "Age": 18},
{"Name": "jinzhu_2", "Age": 20},
})

注意 从映射创建时,不会调用钩子,不会保存关联,也不会回填主键值

从 SQL 表达式/上下文估值器创建

GORM 允许使用 SQL 表达式插入数据,有两种方法可以实现此目标,从 map[string]interface{}自定义数据类型 创建,例如

// Create from 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)"));

// Create from customized data type
type Location struct {
X, Y int
}

// Scan implements the sql.Scanner interface
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)"))

高级

创建关联

在创建一些带有关联的数据时,如果其关联值不是零值,则这些关联将被更新,并且将调用其 Hooks 方法。

type CreditCard struct {
gorm.Model
Number string
UserID uint
}

type User struct {
gorm.Model
Name string
CreditCard CreditCard
}

db.Create(&User{
Name: "jinzhu",
CreditCard: CreditCard{Number: "411111111111"}
})
// INSERT INTO `users` ...
// INSERT INTO `credit_cards` ...

你可以使用 SelectOmit 跳过保存关联,例如

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

// skip all associations
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:(-);"`
}

注意 SQLite 不支持批量插入时某些记录为默认值。
请参阅 SQLite Insert 语句。例如

type Pet struct {
Name string `gorm:"default:cat"`
}

// In SQLite, this is not supported, so GORM will build a wrong SQL to raise error:
// INSERT INTO `pets` (`name`) VALUES ("dog"),(DEFAULT) RETURNING `name`
db.Create(&[]Pet{{Name: "dog"}, {}})

一种可行的替代方法是在钩子中为字段分配默认值,例如

func (p *Pet) BeforeCreate(tx *gorm.DB) (err error) {
if p.Name == "" {
p.Name = "cat"
}
}

您可以在 issues#6335 中查看更多信息

当使用虚拟/生成值时,您可能需要禁用其创建/更新权限,请查看 字段级权限

Upsert / On Conflict

GORM 为不同的数据库提供兼容的 Upsert 支持

import "gorm.io/gorm/clause"

// Do nothing on conflict
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user)

// Update columns to default value on `id` conflict
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

// Use SQL expression
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));

// Update columns to new value on `id` conflict
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

// Update all columns to new value on conflict except primary keys and those columns having default values from sql func
db.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&users)
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age", ...;
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age`=VALUES(age), ...; MySQL

还可以在 高级查询 中查看 FirstOrInitFirstOrCreate

查看 原始 SQL 和 SQL 构建器 了解更多详情

白金赞助商

黄金赞助商

白金赞助商

黄金赞助商