zoukankan      html  css  js  c++  java
  • GORM模型(Model)创建

    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) // 返回插入记录的条数
    
    }
    
    

    image-20211201220710597

    image-20211201220722917

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

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

    image-20211201221432255

    image-20211201221531380

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

    image-20211201221834898

    image-20211201221928581

    四、批量插入

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

    image-20211201222410743

    image-20211201222401759

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

    image-20211201222904657

    image-20211201222929455

    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) // 返回插入记录的条数
    
    }
    
    

    image-20211201223519120

    如果您想跳过 钩子 方法,您可以使用 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 不会被调用,且主键也不会自动填充

    image-20211201224409221

    image-20211201224518631

    七、使用 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"},
    	})
    }
    
    

    image-20211201230607234

    image-20211201230638413

    您也可以通过 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 及构造器 获取更多细节

    在当下的阶段,必将由程序员来主导,甚至比以往更甚。
  • 相关阅读:
    Java中的集合类
    Java中的包装类
    Java中的多线程总结(转)
    Java开发中的23种设计模式详解 (转)
    Java中异常处理和设计
    Jmeter的ForEach控制器
    Jmeter事务控制器
    Jmeter下线程顺序启动
    Jmeter跨线程调用参数
    Jmeter使用Python
  • 原文地址:https://www.cnblogs.com/randysun/p/15626794.html
Copyright © 2011-2022 走看看