GORM模型(Model)创建 - RandySun - 博客园

GORM模型(Model)创建

一、定义模型

`type CreateUsers struct {

gorm.Model
Name         string
Email        *string
Age          uint8
Birthday     *time.Time
MemberNumber sql.NullString
ActivatedAt  sql.NullTime
CreatedAt    time.Time
UpdatedAt    time.Time

}`

二、创建记录

``package main
import (

"database/sql"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
// CREATE TABLE `create_users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,`email` longtext,`a

ge tinyint unsigned,birthday datetime(3) NULL,member_number longtext,activated_at datetime(3) NULL,PRIMARY KEY (id),INDEX idx_create_users_deleted_at (deleted_at`))

db.AutoMigrate(&CreateUsers{})
// 创建数据
timeNow := time.Now()
user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}
//INSERT INTO `create_users` (`created_at`,`updated_at`,`deleted_at`,`name`,`email`,`age`,`birthday`,`member_number`,`activated_at`) VALUES ('2021-12-01 22:04:30.799','2021-12-01 22:04:30.799',N

ULL,'RandySun',NULL,18,'2021-12-01 22:04:30.798',NULL,NULL)

result := db.Debug().Create(&user) // 通过数据的指针来创建
fmt.Println(user.ID)             // 返回插入数据的主键
fmt.Println(result.Error)        // 返回 error
fmt.Println(result.RowsAffected) // 返回插入记录的条数

}``

三、用指定的字段创建记录

3.1 创建记录并更新给出的字段。

``package main
import (

"database/sql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
db.AutoMigrate(&CreateUsers{})
// 创建数据
timeNow := time.Now()
user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}
// 创建指定字段
//  INSERT INTO `create_users` (`created_at`,`updated_at`,`name`,`age`) VALUES ('2021-12-01 22:08:47.894','2021-12-01 22:08:47.894','RandySun',18)
db.Debug().Select("Name", "Age", "CreatedAt").Create(&user)

}``

3.2 创建一个记录且一同忽略传递给略去的字段值。

``package main
import (

"database/sql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
db.AutoMigrate(&CreateUsers{})
// 创建数据
timeNow := time.Now()
user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}
// 插入排除字段
//   INSERT INTO `create_users` (`updated_at`,`deleted_at`,`email`,`birthday`,`member_number`,`activated_at`) VALUES ('2021-12-01 22:17:54.491',NULL,NULL,'2021-12-01 22:17:54.491',NULL,NULL)
db.Debug().Omit("Name", "Age", "CreatedAt").Create(&user)

}``

四、批量插入

4.1 批量创建

要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条SQL语句来插入所有数据,并回填主键的值,钩子方法也会被调用。

``package main
import (

"database/sql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
db.AutoMigrate(&CreateUsers{})
// 创建数据
timeNow := time.Now()
user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}
// 批量插入数据
// INSERT INTO `create_users` (`created_at`,`updated_at`,`deleted_at`,`name`,`email`,`age`,`birthday`,`member_number`,`activated_at`) VALUES ('2021-12-01 22:22:37.647','2021-12-01 22:22:37.647',N

ULL,'RandySun01',NULL,0,NULL,NULL,NULL),('2021-12-01 22:22:37.647','2021-12-01 22:22:37.647',NULL,'RandySun02',NULL,0,NULL,NULL,NULL),('2021-12-01 22:22:37.647','2021-12-01 22:22:37.647',NULL,'RandySun03',NULL,0,NULL,NULL,NULL)

var users = []CreateUsers{{Name: "RandySun01"}, {Name: "RandySun02"}, {Name: "RandySun03"}}
db.Debug().Create(&users)
for _, user := range users {
    fmt.Println(user.ID) // 4,5,6
}

}``

4.2 分批创建

使用 CreateInBatches 分批创建时,你可以指定每批的数量,例如:

`package main
import (

"database/sql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
db.AutoMigrate(&CreateUsers{})
// 创建数据
timeNow := time.Now()
user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}
// 分批创建
var users = []CreateUsers{{Name: "RandySun01"}, {Name: "RandySun02"},{Name: "RandySun04"},{Name: "RandySun0...."}, {Name: "RandySun200000"}}
// 数量为 2
db.Debug().CreateInBatches(users, 2)
for _, user := range users {
    fmt.Println(user.ID) // 4,5,6
}

}`

UpsertCreate With Associations 也支持批量插入

注意 使用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 允许用户定义的钩子有 BeforeSave, BeforeCreate, AfterSave, 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

}`

`package main
import (

"database/sql"
"errors"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
type CreateUsers struct {

gorm.Model
Name         string
Email        *string
Age          uint8
Birthday     *time.Time
MemberNumber sql.NullString
ActivatedAt  sql.NullTime
CreatedAt    time.Time
UpdatedAt    time.Time

}
func (u CreateUsers) BeforeCreate(tx gorm.DB) (err error) {

fmt.Println("创建之前触发钩子")
if u.Name == "RandySun" {
    return errors.New("invalid role")
}
return

}
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
db.AutoMigrate(&CreateUsers{})
// 创建数据
timeNow := time.Now()
user := CreateUsers{Name: "RandySun", Age: 18, Birthday: &timeNow}
// 创建用户
result := db.Debug().Create(&user) // 通过数据的指针来创建
fmt.Println(user.ID)             // 返回插入数据的主键
fmt.Println(result.Error)        // 返回 error
fmt.Println(result.RowsAffected) // 返回插入记录的条数

}`

如果您想跳过 钩子 方法,您可以使用 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{}{} 创建记录,例如:

``package main
import (

"database/sql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
db.AutoMigrate(&CreateUsers{})
// 根据 Map 创建
db.Debug().Model(&CreateUsers{}).Create(map[string]interface{}{
    "Name": "RandySun", "Age": 18,
})
// batch insert from `[]map[string]interface{}{}`
db.Debug().Model(&CreateUsers{}).Create([]map[string]interface{}{
    {"Name": "RandySunMap01", "Age": 18},
    {"Name": "RandySunMap02", "Age": 20},
})

}``

注意: 根据 map 创建记录时,association 不会被调用,且主键也不会自动填充

七、使用 SQL 表达式、Context Valuer 创建记录

GORM 允许使用 SQL 表达式插入数据,有两种方法实现这个目标。根据 map[string]interface{}自定义数据类型 创建,例如:

``// 通过 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 方法实现了 sql.Scanner 接口
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)"))``

八、高级选项

关联创建

创建关联数据时,如果关联值是非零值,这些关联会被 upsert,且它们的 Hook 方法也会被调用

``package main
import (

"database/sql"
"errors"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"

)
type CreditCard struct {

gorm.Model
Number string
UserID uint

}
type CreateUsers struct {

gorm.Model
Name         string
Email        *string
Age          uint8
Birthday     *time.Time
MemberNumber sql.NullString
ActivatedAt  sql.NullTime
CreatedAt    time.Time
UpdatedAt    time.Time
CreditCard   CreditCard `gorm:"foreignKey:ID"`

}
func (u CreateUsers) BeforeCreate(tx gorm.DB) (err error) {

fmt.Println("创建之前触发钩子")
if u.Name == "RandySun" {
    return errors.New("invalid role")
}
return

}
func main() {

dsn := "root:@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
    panic(err)
}
// 迁移表创建对应关系
db.AutoMigrate(&CreateUsers{}, &CreditCard{})
// 关联创建
db.Debug().Create(&CreateUsers{
    Name:       "Randy",
    CreditCard: CreditCard{Number: "34353435"},
})

}``

您也可以通过 SelectOmit 跳过关联保存,例如:

`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()" // 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)
// MERGE INTO "users" USING WHEN NOT MATCHED THEN INSERT WHEN MATCHED THEN UPDATE SET *; SQL Server
// INSERT INTO users ON DUPLICATE KEY UPDATE ; MySQL
// 使用SQL语句
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));
// 在id冲突时,将列更新为新值
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
// 在冲突时,更新除主键以外的所有列到新值。
db.Clauses(clause.OnConflict{
UpdateAll: true,
}).Create(&users)
// INSERT INTO "users" * ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age", ...;``

您还可以查看 高级查询 中的 FirstOrInitFirstOrCreate

查看 原生 SQL 及构造器 获取更多细节

__EOF__

  • 原文作者: RandySun
  • 原文链接: https://www.cnblogs.com/randysun/
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐;)】一下。

原网址: 访问
创建于: 2022-08-16 21:03:10
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论