day11 速通官方文档不可取 |青训营笔记

249 阅读4分钟

题记

这是我参与「第五届青训营 」伴学笔记创作活动的第 11天,本文用于记录在青训营的学习笔记和一些心得。

day11 1月25日

今天没怎么学gorm,在刷力扣,感觉速通文档不可能的,还是先把字节的直播课过一遍,对三件套有基本印象,然后官方文档就慢慢看吧。

6.CRUD接口

创建

创建记录
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
​
result := db.Create(&user) // 通过数据的指针来创建
​
user.ID             // 返回插入数据的主键
result.Error        // 返回 error
result.RowsAffected // 返回插入记录的条数

image-20230125210728998

用指定的字段创建记录

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

image-20230125210915139

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

image-20230125210958353

批量插入

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

image-20230125211200815

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

image-20230125211437494

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
}

首先u.UUID = uuid.New()查了下资料:

  • uuid是Universally Unique Identifier的缩写,即通用唯一识别码。uuid的目的是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 uuid。A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems.
  • 想调用uuid 需要go get -u github.com/satori/go.uuid。

其次该函数只需声明,不用调用,当想要的表执行相应操作,该钩子会自己执行。

如果您想跳过 钩子 方法,您可以使用 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)

image-20230125210153411

根据 Map 创建

GORM 支持根据 map[string]interface{}[]map[string]interface{}{} 创建记录,例如:

image-20230125212015383

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

我这里的结果主键自动递增是因为我设置了自动递增。

image-20230125212333828

使用 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 方法也会被调用

image-20230125213119487

同时会给user和creditCard插入数据。

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

image-20230125213557657

image-20230125213526968

默认值

您可以通过标签 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 支持

代码常看官方文档。

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

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