对数据库进行添加,即新建操作。
这里以User类型作为操作对象:
type User struct {
gorm.Model
Name string
Age int
Birthday time.Time
}先打开数据库:
dsn := "root:root@tcp(IP:PORT)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})创建记录
向数据库中添加一条新记录
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user)
if result.Error != nil {
t.Log("create record failed")
} else {
t.Logf("the primary key of inserted record is : %d", user.ID)
t.Logf("affect rows is: %d", result.RowsAffected)
}直接使用Create即可创建记录,其返回值中Error记录了操作是否成功,RowsAffected记录了影响的数据库行号。
如果插入成功,那么对应的user对象中的ID就会被设置成对应的主键值。
如果在插入之前数据库表没有创建,那么插入会失败,可以通过AutoMigrate来自动创建对应的数据库表
db.AutoMigrate(&User{})用选定的字段创建记录
对于一个model,可以只将其部分字段添加到数据库中创建新记录。
例如,下面创建一个记录并为指定的字段赋值。需要注意以下,虽然Select参数中没有提到的gorm.Model中的字段也会添加。
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函数,效果跟Select相反,忽略传递给Omit的字段的值,也就是说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将生成一条SQL语句来插入所有数据并填充主键值,钩子方法也将被调用。
var users = []User{{Name: "jinzhu1", Birthday: time.Now()}, {Name: "jinzhu2", Birthday: time.Now()}, {Name: "jinzhu3", Birthday: time.Now()}}
result := db.Create(&users)还可以使用CreateInBatches函数来分批次处理,可以自己指定每一批次的大小。
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// batch size 100
db.CreateInBatches(users, 100)注意,如果在GORM初始化的时候就设置了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)Create钩子函数
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
}使用SkipHooks的session模式,如果你想要跳过Hooks方法。
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},
})当从map创建时,钩子不会被调用,关联不会被保存,主键值不会被填充。
根据SQL表达式创建
可以灵活定义字段插入时的数据格式。 使用SQL表达式也有两种方式,一种就是使用Map。
// 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)"));还有就是使用自定义类型,例如下面的类型
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)},
}
}在GormValue中存在对应的SQL语句和vars。
在Create函数中会自动使用指定的SQL语句格式来进行创建值
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)"))进阶
创建关联数据
当创建一些带有关联的数据时,如果关联值不是零值,那么这些关联将同样被更新,其hook方法也会被调用。
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` ...可以通过使用Select,Omit来跳过保存关联数据。例如
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"`
}你必须为数据库中具有默认值的字段设置default标签,如果想要在migrate的时候跳过默认值定义,可以使用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"冲突的时候什么都不做
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user)id冲突的时候将列的值设置为默认值
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.Assignments(map[string]interface{}{"role": "user"}),
}).Create(&users)
// 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));tags: gorm