Create Record
1. user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
3. result := db.Create(&user) // pass pointer of data to Create
5. user.ID // returns inserted data's primary key
6. result.Error // returns error
7. result.RowsAffected // returns inserted records count
Create Record With Selected Fields
Create a record and assign a value to the fields specified.
1. db.Select("Name", "Age", "CreatedAt").Create(&user)
2. // INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
Create a record and ignore the values for fields passed to omit.
1. db.Omit("Name", "Age", "CreatedAt").Create(&user)
2. // INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")
Batch Insert
To efficiently insert large number of records, pass a slice to the Create method. GORM will generate a single SQL statement to insert all the data and backfill primary key values, hook methods will be invoked too.
1. var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
2. db.Create(&users)
4. for _, user := range users {
5. user.ID // 1,2,3
6. }
You can specify batch size when creating with CreateInBatches, e.g:
1. var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
3. // batch size 100
4. db.CreateInBatches(users, 100)
Batch Insert is also supported when using Upsert and Create With Associations
NOTE initialize GORM with
CreateBatchSizeoption, allINSERTwill respect this option when creating record & associations
1. db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
2. CreateBatchSize: 1000,
3. })
5. db := db.Session(&gorm.Session{CreateBatchSize: 1000})
7. users = [5000]User{{Name: "jinzhu", Pets: []Pet{pet1, pet2, pet3}}...}
9. db.Create(&users)
10. // INSERT INTO users xxx (5 batches)
11. // INSERT INTO pets xxx (15 batches)
Create Hooks
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
1. func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
2. u.UUID = uuid.New()
4. if u.Role == "admin" {
5. return errors.New("invalid role")
6. }
7. return
8. }
If you want to skip Hooks methods, you can use the SkipHooks session mode, for example:
1. DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)
3. DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)
5. DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)
Create From Map
GORM supports create from map[string]interface{} and []map[string]interface{}{}, e.g:
1. db.Model(&User{}).Create(map[string]interface{}{
2. "Name": "jinzhu", "Age": 18,
3. })
5. // batch insert from `[]map[string]interface{}{}`
6. db.Model(&User{}).Create([]map[string]interface{}{
7. {"Name": "jinzhu_1", "Age": 18},
8. {"Name": "jinzhu_2", "Age": 20},
9. })
NOTE When creating from map, hooks won’t be invoked, associations won’t be saved and primary key values won’t be back filled
Create From SQL Expression/Context Valuer
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:
1. // Create from map
2. db.Model(User{}).Create(map[string]interface{}{
3. "Name": "jinzhu",
4. "Location": clause.Expr{SQL: "ST_PointFromText(?)", Vars: []interface{}{"POINT(100 100)"}},
5. })
6. // INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"));
8. // Create from customized data type
9. type Location struct {
10. X, Y int
11. }
13. // Scan implements the sql.Scanner interface
14. func (loc *Location) Scan(v interface{}) error {
15. // Scan a value into struct from database driver
16. }
18. func (loc Location) GormDataType() string {
19. return "geometry"
20. }
22. func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
23. return clause.Expr{
24. SQL: "ST_PointFromText(?)",
25. Vars: []interface{}{fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y)},
26. }
27. }
29. type User struct {
30. Name string
31. Location Location
32. }
34. db.Create(&User{
35. Name: "jinzhu",
36. Location: Location{X: 100, Y: 100},
37. })
38. // INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))
Advanced
Create With Associations
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.
1. type CreditCard struct {
2. gorm.Model
3. Number string
4. UserID uint
5. }
7. type User struct {
8. gorm.Model
9. Name string
10. CreditCard CreditCard
11. }
13. db.Create(&User{
14. Name: "jinzhu",
15. CreditCard: CreditCard{Number: "411111111111"}
16. })
17. // INSERT INTO `users` ...
18. // INSERT INTO `credit_cards` ...
You can skip saving associations with Select, Omit, for example:
1. db.Omit("CreditCard").Create(&user)
3. // skip all associations
4. db.Omit(clause.Associations).Create(&user)
Default Values
You can define default values for fields with tag default, for example:
1. type User struct {
2. ID int64
3. Name string `gorm:"default:galeone"`
4. Age int64 `gorm:"default:18"`
5. }
Then the default value will be used when inserting into the database for zero-value fields
NOTE Any zero value like
0,'',falsewon’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:
1. type User struct {
2. gorm.Model
3. Name string
4. Age *int `gorm:"default:18"`
5. Active sql.NullBool `gorm:"default:true"`
6. }
NOTE You have to setup the
defaulttag for fields having default or virtual/generated value in database, if you want to skip a default value definition when migrating, you could usedefault:(-), for example:
1. type User struct {
2. ID string `gorm:"default:uuid_generate_v3()"` // db func
3. FirstName string
4. LastName string
5. Age uint8
6. FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
7. }
When using virtual/generated value, you might need to disable its creating/updating permission, check out Field-Level Permission
Upsert / On Conflict
GORM provides compatible Upsert support for different databases
1. import "gorm.io/gorm/clause"
3. // Do nothing on conflict
4. db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user)
6. // Update columns to default value on `id` conflict
7. db.Clauses(clause.OnConflict{
8. Columns: []clause.Column{{Name: "id"}},
9. DoUpdates: clause.Assignments(map[string]interface{}{"role": "user"}),
10. }).Create(&users)
11. // MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET ***; SQL Server
12. // INSERT INTO `users` *** ON DUPLICATE KEY UPDATE ***; MySQL
14. // Use SQL expression
15. db.Clauses(clause.OnConflict{
16. Columns: []clause.Column{{Name: "id"}},
17. DoUpdates: clause.Assignments(map[string]interface{}{"count": gorm.Expr("GREATEST(count, VALUES(count))")}),
18. }).Create(&users)
19. // INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `count`=GREATEST(count, VALUES(count));
21. // Update columns to new value on `id` conflict
22. db.Clauses(clause.OnConflict{
23. Columns: []clause.Column{{Name: "id"}},
24. DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
25. }).Create(&users)
26. // MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
27. // INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
28. // INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age=VALUES(age); MySQL
30. // Update all columns, except primary keys, to new value on conflict
31. db.Clauses(clause.OnConflict{
32. UpdateAll: true,
33. }).Create(&users)
34. // INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age", ...;
Also checkout FirstOrInit, FirstOrCreate on Advanced Query
Checkout Raw SQL and SQL Builder for more details
