CRUD接口--创建

63 阅读4分钟

创建

创建记录

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)

使用UpsertCreate 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允许为BeforeSaveBeforeCreateAfterSave,和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` ...

您可以使用SelectOmit跳过保存关联,例如:

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中签出FirstOrInitFirstOrCreate

有关详细信息,请查看原生 SQL 和 SQL 生成器