CRUD Interface

Create

qianmoQqianmoQ· 更新于 2026-09-13· 阅读 20 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

Create Record

Generics API

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


ctx := context.Background()
err := gorm.G[User](db).Create(ctx, &user) 


result := gorm.WithResult()
err := gorm.G[User](db, result).Create(ctx, &user)
user.ID             
result.Error        
result.RowsAffected 

Traditional API

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

result := db.Create(&user) 

user.ID             
result.Error        
result.RowsAffected 

We can also create multiple records with Create():

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

result := db.Create(users) 

result.Error        
result.RowsAffected 

NOTE You cannot pass a struct to ‘create’, so you should pass a pointer to the data.

Create Record With Selected Fields

Create a record and assign a value to the fields specified.

db.Select("Name", "Age", "CreatedAt").Create(&user)

Create a record and ignore the values for fields passed to omit.

db.Omit("Name", "Age", "CreatedAt").Create(&user)

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. It will begin a transaction when records can be split into multiple batches.

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

for _, user := range users {
  user.ID 
}

You can specify batch size when creating with CreateInBatches, e.g:

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


db.CreateInBatches(users, 100)

Batch Insert is also supported when using Upsert and Create With Associations

NOTE initialize GORM with CreateBatchSize option, all INSERT will respect this option when creating record & associations

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)

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

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:

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)

Create From Map

GORM supports create from map[string]interface{} and []map[string]interface{}{}, e.g:

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


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

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:


db.Model(User{}).Create(map[string]interface{}{
  "Name": "jinzhu",
  "Location": clause.Expr{SQL: "ST_PointFromText(?)", Vars: []interface{}{"POINT(100 100)"}},
})



type Location struct {
  X, Y int
}


func (loc *Location) Scan(v interface{}) error {
  
}

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},
})

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.

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"}
})

You can skip saving associations with Select, Omit, for example:

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


db.Omit(clause.Associations).Create(&user)

Default Values

You can define default values for fields with tag default, for example:

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 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:

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

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:

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

NOTE SQLite doesn’t support some records are default values when batch insert.
See SQLite Insert stmt. For example:

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

db.Create(&[]Pet{{Name: "dog"}, {}})


A viable alternative is to assign default value to fields in the hook, e.g.

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


You can see more info in [issues#6335](https://github.com/go-gorm/gorm/issues/6335)

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

import "gorm.io/gorm/clause"


db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user)


db.Clauses(clause.OnConflict{
  Columns:   []clause.Column{{Name: "id"}},
  DoUpdates: clause.Assignments(map[string]interface{}{"role": "user"}),
}).Create(&users)




db.Clauses(clause.OnConflict{
  Columns:   []clause.Column{{Name: "id"}},
  DoUpdates: clause.Assignments(map[string]interface{}{"count": gorm.Expr("GREATEST(count, VALUES(count))")}),
}).Create(&users)



db.Clauses(clause.OnConflict{
  Columns:   []clause.Column{{Name: "id"}},
  DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&users)





db.Clauses(clause.OnConflict{
  UpdateAll: true,
}).Create(&users)

Also checkout FirstOrInit, FirstOrCreate on Advanced Query

Checkout Raw SQL and SQL Builder for more details

GitHub tag (latest SemVer)

评论

登录后参与评论

正在加载评论…