创建
创建记录
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 通过数据的指针来创建
user.ID // 返回插入数据的主键
result.Error // 返回
error result.RowsAffected // 返回插入记录的条数
我们还可以使用 Create() 创建多项记录:
users := []*User{
User{Name: "Jinzhu", Age: 18, Birthday: time.Now()},
User{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和Create With Associations时也支持Batch Insert
注意 使用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
}
如果要跳过 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)
根据 Map 创建
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 表达式、Context Valuer 创建记录
GORM允许使用SQL表达式插入数据,有两种方法可以实现这一目标,从map[string]interface{}或Customized Data Types,例如:
// 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` ...
您可以使用Select、Omit跳过保存关联,例如:
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"`
}
然后,将zero-value插入数据库时将使用默认值
注意 对于那些字段定义的默认值,任何零值(如 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"
// 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
同时在Advanced Query中签出FirstOrInit, FirstOrCreate
有关详细信息,请查看原生 SQL 和 SQL 生成器