user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()} result := db.Create(&user) // 通过数据的指针来创建 user.ID // 返回插入数据的主键 result.Error // 返回 error result.RowsAffected // 返回插入记录的条数
我们还可以使用 Create() 创建多项记录:
Create()
users := []*User{ User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}, User{Name: "Jackson", Age: 19, Birthday: time.Now()}, } result := db.Create(users) // 传递切片以插入多行数据 result.Error // 返回 error result.RowsAffected // 返回插入记录的条数
{% note warn %} NOTE 你无法向 'create' 传递结构体,所以你应该传入数据的指针. {% endnote %}
创建记录并为指定字段赋值。
db.Select("Name", "Age", "CreatedAt").Create(&user) // INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
创建记录并忽略传递给 'Omit' 的字段值
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 will generate a single SQL statement to insert all the data and backfill primary key values, hook methods will be invoked too. 当记录可以分成多个批处理时,它将开始一个 交易。
Create
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}} db.Create(&users) for _, user := range users { user.ID // 1,2,3 }
You can specify batch size when creating with CreateInBatches, e.g:
CreateInBatches
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}} // batch size 100 db.CreateInBatches(users, 100)
Batch Insert is also supported when using Upsert and Create With Associations
{% note warn %} NOTE initialize GORM with CreateBatchSize option, all INSERT will respect this option when creating record & associations {% endnote %}
CreateBatchSize
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 allows user defined hooks to be implemented for BeforeSave, BeforeCreate, AfterSave, AfterCreate. These hook method will be called when creating a record, refer Hooks for details on the lifecycle
BeforeSave
BeforeCreate
AfterSave
AfterCreate
func (u *User) BeforeCreate(tx *gorm.DB) (err error) { u.UUID = uuid.New() if u.Role == "admin" { return errors.New("invalid role") } return }
If you want to skip Hooks methods, you can use the SkipHooks session mode, for example:
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 supports create from map[string]interface{} and []map[string]interface{}{}, e.g:
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}, })
{% note warn %} NOTE When creating from map, hooks won't be invoked, associations won't be saved and primary key values won't be back filled {% endnote %}
GORM allows insert data with SQL expression, there are two ways to achieve this goal, create from map[string]interface{} or Customized Data Types, for example:
// 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)"))
When creating some data with associations, if its associations value is not zero-value, those associations will be upserted, and its Hooks methods will be invoked.
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` ...
You can skip saving associations with Select, Omit, for example:
Select
Omit
db.Omit("CreditCard").Create(&user) // skip all associations db.Omit(clause.Associations).Create(&user)
You can define default values for fields with tag default, for example:
default
type User struct { ID int64 Name string `gorm:"default:galeone"` Age int64 `gorm:"default:18"` }
Then the default value will be used when inserting into the database for zero-value fields
{% note warn %} NOTE Any zero value like 0, '', false won't be saved into the database for those fields defined default value, you might want to use pointer type or Scanner/Valuer to avoid this, for example: {% endnote %}
0
''
false
type User struct { gorm.Model Name string Age *int `gorm:"default:18"` Active sql.NullBool `gorm:"default:true"` }
{% note warn %} NOTE You have to setup the default tag for fields having default or virtual/generated value in database, if you want to skip a default value definition when migrating, you could use default:(-), for example: {% endnote %}
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:(-);"` }
When using virtual/generated value, you might need to disable its creating/updating permission, check out Field-Level Permission
GORM provides compatible Upsert support for different databases
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
Also checkout FirstOrInit, FirstOrCreate on Advanced Query
FirstOrInit
FirstOrCreate
Checkout Raw SQL and SQL Builder for more details
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8