把数据存进数据库,是最基础的操作。GORM 提供了多种创建记录的方式,适应不同场景。
定义好模型后,用 Create 方法插入:
type User struct {
ID uint
Name string
Age int
}
user := User{Name: "张三", Age: 25}
result := db.Create(&user)
fmt.Println(user.ID)
fmt.Println(result.Error)
fmt.Println(result.RowsAffected)
注意 Create 的参数是指针。GORM 会回填主键 ID,所以传入指针才能拿到这个值。
result 是 *gorm.DB 类型,可以获取:
Error - 错误信息RowsAffected - 影响行数不想定义结构体,或者只想插入部分字段,可以用 map:
db.Model(&User{}).Create(map[string]interface{}{
"Name": "李四",
"Age": 30,
})
批量插入多条:
users := []map[string]interface{}{
{"Name": "用户1", "Age": 20},
{"Name": "用户2", "Age": 25},
{"Name": "用户3", "Age": 30},
}
db.Model(&User{}).Create(users)
插入大量数据时,一条条插入效率太低:
users := []User{
{Name: "用户1", Age: 20},
{Name: "用户2", Age: 25},
{Name: "用户3", Age: 30},
}
db.Create(&users)
GORM 会生成一条 INSERT 语句,包含所有数据。但不同数据库对单条 SQL 的参数数量有限制,数据量大时要分批:
db.CreateInBatches(users, 100)
每批 100 条,避免 SQL 过长。
只想插入部分字段,忽略其他:
db.Select("Name").Create(&User{
Name: "王五",
Age: 28,
})
生成的 SQL 只包含 name 字段,age 使用数据库默认值。
排除某些字段:
db.Omit("Age").Create(&User{
Name: "赵六",
Age: 28,
})
效果相同,只是写法不同。
临时指定表名:
db.Table("vip_users").Create(&user)
或者用 Model 方法:
db.Model(&User{}).Table("vip_users").Create(&user)
自增主键会自动回填:
user := User{Name: "测试"}
db.Create(&user)
fmt.Println(user.ID)
如果主键不是自增,比如 UUID,需要在创建前赋值:
import "github.com/google/uuid"
user := User{
ID: uuid.New(),
Name: "测试",
}
db.Create(&user)
模型字段可以设置默认值:
type User struct {
ID uint
Name string
Age int `gorm:"default:18"`
Role string `gorm:"default:'user'"`
}
插入时不指定这些字段,会使用默认值:
user := User{Name: "测试"}
db.Create(&user)
注意:default 标签的值是 SQL 层面的默认值,不是 Go 层面的。如果 Go 结构体字段有零值,GORM 仍然会插入这个零值,而不是使用数据库默认值。
要使用数据库默认值,需要显式忽略:
db.Omit("Age").Create(&user)
或者用 default 标签配合指针类型:
type User struct {
Age *int `gorm:"default:18"`
}
MySQL 的 ON DUPLICATE KEY UPDATE,PostgreSQL 的 ON CONFLICT,GORM 统一用 Clauses 处理:
import "gorm.io/gorm/clause"
user := User{ID: 1, Name: "更新后的名字", Age: 30}
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&user)
主键冲突时更新指定字段。
更新所有字段:
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&user)
不更新,直接忽略:
db.Clauses(clause.OnConflict{
DoNothing: true,
}).Create(&user)
创建记录时会触发钩子:
func (u *User) BeforeCreate(tx *gorm.DB) error {
if u.Name == "" {
return errors.New("名字不能为空")
}
return nil
}
钩子返回错误会中止创建操作。
主键冲突
Error 1062: Duplicate entry '1' for key 'PRIMARY'
主键已存在,使用 OnConflict 处理或检查后插入。
字段过长
Error 1406: Data too long for column 'name' at row 1
数据超出字段长度限制,检查模型定义或数据内容。
非空约束
Error 1048: Column 'name' cannot be null
必填字段未赋值,检查模型或添加默认值。
创建记录看似简单,但批量插入、冲突处理、默认值这些细节,用好了能提升性能和代码质量。