CRUD 接口

创建

qianmoQqianmoQ· 更新于 2026-09-23· 阅读 16 分钟· 0 次阅读

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

创建记录

泛型 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 

传统 API

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

result := db.Create(&user) 

user.ID             
result.Error        
result.RowsAffected 

我们也可以使用 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 

注意 不能将结构体直接传递给 Create,应当传递指向数据的指针。

选择字段创建记录

创建记录并为指定字段赋值。

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

创建记录并忽略传递给 Omit 的字段的值。

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

批量插入

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

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

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

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

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


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)

创建钩子

GORM 允许用户为 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
}

如果想跳过 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,
})


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

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

从 SQL 表达式/Context Valuer 创建

GORM 支持使用 SQL 表达式插入数据,有两种方式可以实现:从 map[string]interface{} 创建或使用自定义数据类型,例如:


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

高级用法

通过关联创建

创建带有关联的数据时,如果关联值不为零值,这些关联将被 upsert,并且会调用其 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"}
})

可以使用 Select、Omit 跳过关联的保存,例如:

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


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

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


一种可行的替代方案是在钩子中为字段赋默认值,例如:

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


更多信息请参见 [issues#6335](https://github.com/go-gorm/gorm/issues/6335)

使用虚拟/生成值时,可能需要禁用其创建/更新权限,请参阅字段级权限。

Upsert / On Conflict

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

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)

也可以查看高级查询中的 FirstOrInit、FirstOrCreate。

更多详情请参阅 原始 SQL 和 SQL 构建器。

GitHub tag (latest SemVer)

评论

登录后参与评论

正在加载评论…