Files
rikako-note/Golang/gorm.md
2025-01-15 22:59:21 +08:00

11 KiB
Raw Blame History

GORM

QuickStart

GORM为golang的orm框架其使用示例如下

package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

type Product struct {
  gorm.Model
  Code  string
  Price uint
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // Migrate the schema
  db.AutoMigrate(&Product{})

  // Create
  db.Create(&Product{Code: "D42", Price: 100})

  // Read
  var product Product
  db.First(&product, 1) // find product with integer primary key
  db.First(&product, "code = ?", "D42") // find product with code D42

  // Update - update product's price to 200
  db.Model(&product).Update("Price", 200)
  // Update - update multiple fields
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

  // Delete - delete product
  db.Delete(&product, 1)
}

Declaring Models

gorm中Model通过struct来进行定义struct中的fields可以为如下类型

  • 基础golang类型
  • 指针
  • type alias
  • 自定义类型
    • 自定义类型需要实现database/sql中的Scanner和Valuer接口

如下为一个Model定义的示例

type User struct {
  ID           uint           // Standard field for the primary key
  Name         string         // A regular string field
  Email        *string        // A pointer to a string, allowing for null values
  Age          uint8          // An unsigned 8-bit integer
  Birthday     *time.Time     // A pointer to time.Time, can be null
  MemberNumber sql.NullString // Uses sql.NullString to handle nullable strings
  ActivatedAt  sql.NullTime   // Uses sql.NullTime for nullable time fields
  CreatedAt    time.Time      // Automatically managed by GORM for creation time
  UpdatedAt    time.Time      // Automatically managed by GORM for update time
  ignored      string         // fields that aren't exported are ignored
}

在上述Model中

  • 基础类型的字段可以直接访问,例如uint, uint8, string
  • 指针类型,例如*time.Time, *string等,代表该字段值可为空
  • sql/database中的sql.NullStringsql.NullTime类型,也代表该字段可为空
  • CreatedAt字段和UpdatedAt字段是由Gorm管理的字段当Record被创建或更新时该字段的值会自动被注入为当前时间
  • Non-exported fields(首字母不是大写)并不会被映射

约定

  • Primary Key: 对于每个Modelgorm中将name为ID的field看作默认primary key
  • Table names 默认情况下gorm将struct name转换为snake_case,并将snake_case复数化作为表名。例如,Userstruct对应的表名为usersGormUserName对应的表名为gorm_user_names
  • Column Names: gorm会自动将field name转化为snake_case,用于表中字段名的映射
  • Timestamp Field: gorm默认使用CreatedAtUpdatedAt字段来跟踪数据的创建和更新日期。

上述是gorm管理实体时的默认约定但是除了默认约定外gorm也支持对实体进行自定义配置。

gorm.Model

gorm提供了一个预先定义的实体gorm.Model,其中包含了常用字段:

// gorm.Model definition
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

在自定义实体时可以将Model实体作为一个field包含在自定义实体中那么Model中定义的实体也会自动包含在自定义实体中。

上述预定义字段含义如下:

  • ID:每行数据的唯一标识符
  • CreatedAt:行创建时间
  • UpdatedAt:行最后更新时间
  • DeletedAt:用于逻辑删除

Advanced

Field Level Permission

对于Exported Fields以大写开头的Field在执行CRUD时GORM默认用友所有权限。但是gorm允许通过tag来修改field level permission故而可以将field指定为read-only, write-only, create-only, update-only, ignored

当字段被标记为ignored若使用gorm migrator创建表时ignored fields不会被创建

type User struct {
  Name string `gorm:"<-:create"` // allow read and create
  Name string `gorm:"<-:update"` // allow read and update
  Name string `gorm:"<-"`        // allow read and write (create and update)
  Name string `gorm:"<-:false"`  // allow read, disable write permission
  Name string `gorm:"->"`        // readonly (disable write permission unless it configured)
  Name string `gorm:"->;<-:create"` // allow read and create
  Name string `gorm:"->:false;<-:create"` // createonly (disabled read from db)
  Name string `gorm:"-"`            // ignore this field when write and read with struct
  Name string `gorm:"-:all"`        // ignore this field when write, read and migrate with struct
  Name string `gorm:"-:migration"`  // ignore this field when migrate with struct
}

CreatedAt / UpdatedAt

gorm默认使用CreatedAt/UpdatedAt来跟踪record创建和更新时间如果这些字段被定义将会在创建/更新操作时设置这些字段。

如果想要为Create/Update字段指定不同的名称可以为自定义字段指定autoCreateTimeautoUpdateTime tag。

如果想要为时间戳指定不同精度,可以按照如下格式进行定义:

type User struct {
  CreatedAt time.Time // Set to current time if it is zero on creating
  UpdatedAt int       // Set to current unix seconds on updating or if it is zero on creating
  Updated   int64 `gorm:"autoUpdateTime:nano"` // Use unix nano seconds as updating time
  Updated   int64 `gorm:"autoUpdateTime:milli"`// Use unix milli seconds as updating time
  Created   int64 `gorm:"autoCreateTime"`      // Use unix seconds as creating time
}

struct embedded

在gorm中如果想要嵌套类可以使用如下方式

  • 将匿名struct作为field
    type Author struct {
      Name  string
      Email string
    }
    
    type Blog struct {
      Author
      ID      int
      Upvotes int32
    }
    // equals
    type Blog struct {
      ID      int64
      Name    string
      Email   string
      Upvotes int32
    }
    
  • 将非匿名struct用gorm:"embedded"进行标记:
    type Author struct {
      Name  string
      Email string
    }
    
    type Blog struct {
      ID      int
      Author  Author `gorm:"embedded"`
      Upvotes int32
    }
    // equals
    type Blog struct {
      ID    int64
      Name  string
      Email string
      Upvotes  int32
    }
    
  • 除了指定gorm:"embeded"tag外还可以为嵌套struct指定prefix
    type Blog struct {
      ID      int
      Author  Author `gorm:"embedded;embeddedPrefix:author_"`
      Upvotes int32
    }
    // equals
    type Blog struct {
      ID          int64
      AuthorName  string
      AuthorEmail string
      Upvotes     int32
    }
    

field tags

当定义model时tag是可选的gorm支持tag如下所示。tag是大小写不敏感的更推荐使用驼峰的写法。

如果需要指定多个tags可以将多个tags根据;进行分隔gorm参数值的转义符为\字符。

Tag Name Description
column column db name
type column data type, prefer to use compatible general type, e.g: bool, int, uint, float, string, time, bytes, which works for all databases, and can be used with other tags together, like not null, size, autoIncrement… specified database data type like varbinary(8) also supported, when using specified database data type, it needs to be a full database data type, for example: MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT
serializer specifies serializer for how to serialize and deserialize data into db, e.g: serializer:json/gob/unixtime
size specifies column data size/length, e.g: size:256
primaryKey specifies column as primary key
unique specifies column as unique
default specifies column default value
precision specifies column precision
scale specifies column scale
not null specifies column as NOT NULL
autoIncrement specifies column auto incrementable
autoIncrementIncrement auto increment step, controls the interval between successive column values
embedded embed the field
embeddedPrefix column name prefix for embedded fields
autoCreateTime track current time when creating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoCreateTime:nano
autoUpdateTime track current time when creating/updating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoUpdateTime:milli
index create index with options, use same name for multiple fields creates composite indexes, refer Indexes for details
uniqueIndex same as index, but create uniqued index
check creates check constraint, eg: check:age > 13, refer Constraints
<- set fields write permission, <-:create create-only field, <-:update update-only field, <-:false no write permission, <- create and update permission
-> set fields read permission, ->:false no read permission
- ignore this field, - no read/write permission, -:migration no migrate permission, -:all no read/write/migrate permission
comment add comment for field when migration