zoukankan      html  css  js  c++  java
  • GORM模型(Model)定义

    GORM模型(Model)定义

    在使用ORM工具时,通常我们需要在代码中定义模型(Models)与数据库中的数据表进行映射,在GORM中模型(Models)通常是正常定义的结构体、基本的go类型或它们的指针。 同时也支持sql.Scannerdriver.Valuer接口(interfaces)。

    GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间

    遵循 GORM 已有的约定,可以减少您的配置和代码量。如果约定不符合您的需求,GORM 允许您自定义配置它们

    一、gorm.Model

    为了方便模型定义,GORM内置了一个gorm.Model结构体。gorm.Model是一个包含了ID, CreatedAt, UpdatedAt, DeletedAt四个字段的Golang结构体。

    // gorm.Model 定义
    type Model struct {
      ID        uint `gorm:"primary_key"`
      CreatedAt time.Time
      UpdatedAt time.Time
      DeletedAt *time.Time
    }
    

    你可以将它嵌入到你自己的模型中:

    // 将 `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`字段注入到`User`模型中
    type User struct {
      gorm.Model
      Name string
    }
    
    // 等效于
    type User struct {
      ID        uint           `gorm:"primaryKey"`
      CreatedAt time.Time
      UpdatedAt time.Time
      DeletedAt gorm.DeletedAt `gorm:"index"`
      Name string
    }
    

    当然也可以完全自己定义模型:

    // 不使用gorm.Model,自行定义模型
    type User struct {
      ID   int
      Name string
    }
    

    创建/更新时间追踪(纳秒、毫秒、秒、Time)

    GORM 约定使用 CreatedAtUpdatedAt 追踪创建/更新时间。如果您定义了这种字段,GORM 在创建、更新时会自动填充 当前时间

    要使用不同名称的字段,您可以配置 autoCreateTimeautoUpdateTime 标签

    如果您想要保存 UNIX(毫/纳)秒时间戳,而不是 time,您只需简单地将 time.Time 修改为 int 即可

    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
    }
    

    对于正常的结构体字段,你也可以通过标签 embedded 将其嵌入,例如:

    type Author struct {
        Name  string
        Email string
    }
    
    type Blog struct {
      ID      int
      Author  Author `gorm:"embedded"`
      Upvotes int32
    }
    // 等效于
    type Blog struct {
      ID    int64
      Name  string
      Email string
      Upvotes  int32
    }
    

    并且,您可以使用标签 embeddedPrefix 来为 db 中的字段名添加前缀,例如:

    type Blog struct {
    	ID      int
    	Author  Author `gorm:"embedded;embeddedPrefix:author_"`
    	Upvotes int32
    }
    
    // 等效于
    type Blog struct {
    	ID          int64
    	AuthorName  string
    	AuthorEmail string
    	Upvotes     int32
    }
    

    二、字段级权限控制

    可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样您就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

    注意: 使用 GORM Migrator 创建表时,不会创建被忽略的字段

    type User struct {
    	Name string `gorm:"<-:create"`          // 允许读和创建
    	Name string `gorm:"<-:update"`          // 允许读和更新
    	Name string `gorm:"<-"`                 // 允许读和写(创建和更新)
    	Name string `gorm:"<-:false"`           // 允许读,禁止写
    	Name string `gorm:"->"`                 // 只读(除非有自定义配置,否则禁止写)
    	Name string `gorm:"->;<-:create"`       // 允许读和写
    	Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
    	Name string `gorm:"-"`                  // 通过 struct 读写会忽略该字段
    }
    

    四、结构体标记(tags)

    使用结构体声明模型时,标记(tags)是可选项。gorm支持以下标记:

    4.1 支持的结构体标记(Struct tags)字段标签

    声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格

    标签名 说明
    column 指定 db 列名
    type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
    size 指定列大小,例如:size:256
    primaryKey 指定列为主键
    unique 指定列为唯一
    default 指定列的默认值
    precision 指定列的精度
    scale 指定列大小
    not null 指定列为 NOT NULL
    autoIncrement 指定列为自动增长
    autoIncrementIncrement 自动步长,控制连续记录之间的间隔
    embedded 嵌套字段
    embeddedPrefix 嵌入字段的列名前缀
    autoCreateTime 创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
    autoUpdateTime 创建/更新时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
    index 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引获取详情
    uniqueIndex index 相同,但创建的是唯一索引
    check 创建检查约束,例如 check:age > 13,查看 约束 获取详情
    <- 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
    -> 设置字段读的权限,->:false 无读权限
    - 忽略该字段,- 无读写权限
    comment 迁移时为字段添加注释

    4.2 关联标签

    标签 描述
    foreignKey 指定当前模型的列作为连接表的外键
    references 指定引用表的列名,其将被映射为连接表外键
    polymorphic 指定多态类型,比如模型名
    polymorphicValue 指定多态值、默认表名
    many2many 指定连接表表名
    joinForeignKey 指定连接表的外键列名,其将被映射到当前表
    joinReferences 指定连接表的外键列名,其将被映射到引用表
    constraint 关系约束,例如:OnUpdateOnDelete

    五、模型定义示例

    package main
    
    import (
    	"database/sql"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"time"
    )
    
    type User struct {
    	gorm.Model
    	Name         string
    	Age          sql.NullInt64
    	Birthday     *time.Time
    	Email        string  `gorm:"type:varchar(100);unique_index"`
    	Role         string  `gorm:"size:255"`        // 设置字段大小为255
    	MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
    	Num          int     `gorm:"AUTO_INCREMENT"`  // 设置 num 为自增类型
    	Address      string  `gorm:"index:addr"`      // 给address字段创建名为addr的索引
    	IgnoreMe     int     `gorm:"-"`               // 忽略本字段
    }
    
    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(&User{})
    
    }
    
    

    image-20211120192155198

    修改其中要给字段

    package main
    
    import (
    	"database/sql"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"time"
    )
    
    type User struct {
    	gorm.Model
    	Name         string `gorm:"type:varchar(100)"` // 增减字段限制
    	Age          sql.NullInt64
    	Birthday     *time.Time
    	Email        string  `gorm:"type:varchar(100);unique_index"`
    	Role         string  `gorm:"size:255"`        // 设置字段大小为255
    	MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
    	Num          int     `gorm:"AUTO_INCREMENT"`  // 设置 num 为自增类型
    	Address      string  `gorm:"index:addr"`      // 给address字段创建名为addr的索引
    	IgnoreMe     int     `gorm:"-"`               // 忽略本字段
    }
    
    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(&User{})
    
    }
    
    

    image-20211120192444934

    image-20211120192518753

    六、主键、表名、列名的约定

    6.1 主键(Primary Key)

    GORM 默认会使用名为ID的字段作为表的主键。

    type User struct {
      ID   string // 名为`ID`的字段会默认作为表的主键
      Name string
    }
    
    // 使用`AnimalID`作为主键
    // Animal 使用`AnimalID`作为主键
    type Animal struct {
    	AnimalID int64 `gorm:"primary_key"`
    	Name     string `gorm:"type:varchar(100);comment:姓名"`
    	Age      int64
    }
    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(&User{}, &Animal{})
    }
    
    

    image-20211120193350811

    6.2 表名(Table Name)

    表名默认就是结构体名称的复数,例如:

    package main
    
    import (
    	"database/sql"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"time"
    )
    
    type User struct {
    	gorm.Model
    	Name         string `gorm:"type:varchar(100);comment:姓名"`
    	Age          sql.NullInt64
    	Birthday     *time.Time
    	Email        string  `gorm:"type:varchar(100);unique_index"`
    	Role         string  `gorm:"size:255"`        // 设置字段大小为255
    	MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
    	Num          int     `gorm:"AUTO_INCREMENT"`  // 设置 num 为自增类型
    	Address      string  `gorm:"index:addr"`      // 给address字段创建名为addr的索引
    	IgnoreMe     int     `gorm:"-"`               // 忽略本字段
    }
    
    // TableName 将 User 的表名设置为 `profiles`
    func (User) TableName() string {
    	return "user_randy"
    }
    
    func (u User) TableName() string {
      if u.Role == "admin" {
        return "admin_users"
      } else {
        return "users"
      }
    }
    
    
    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)
    	}
        // 禁用默认表名的复数形式,如果置为 true,则 `User` 的默认表名是 `user`
    	db.SingularTable(true)
        
    	// 迁移表创建对应关系
    	db.AutoMigrate(&User{},)
    
    }
    
    

    image-20211120194727425

    也可以通过Table()指定表名:

    // 使用User结构体创建名为`deleted_users`的表
    db.Table("deleted_users").CreateTable(&User{})
    
    var deleted_users []User
    db.Table("deleted_users").Find(&deleted_users)
    //// SELECT * FROM deleted_users;
    
    db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
    //// DELETE FROM deleted_users WHERE name = 'jinzhu';
    

    GORM还支持更改默认表名称规则:

    gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string  {
      return "prefix_" + defaultTableName;
    }
    

    6.3 列名(Column Name)

    列名由字段名称进行下划线分割来生成

    type User struct {
      ID        uint      // column name is `id`
      Name      string    // column name is `name`
      Birthday  time.Time // column name is `birthday`
      CreatedAt time.Time // column name is `created_at`
    }
    

    可以使用结构体tag指定列名:

    type Animal struct {
      AnimalId    int64     `gorm:"column:beast_id"`         // set column name to `beast_id`
      Birthday    time.Time `gorm:"column:day_of_the_beast"` // set column name to `day_of_the_beast`
      Age         int64     `gorm:"column:age_of_the_beast"` // set column name to `age_of_the_beast`
    }
    

    6.3 时间戳跟踪

    CreatedAt

    如果模型有 CreatedAt字段,该字段的值将会是初次创建记录的时间。

    db.Create(&user) // `CreatedAt`将会是当前时间
    
    // 可以使用`Update`方法来改变`CreateAt`的值
    db.Model(&user).Update("CreatedAt", time.Now())
    

    UpdatedAt

    如果模型有UpdatedAt字段,该字段的值将会是每次更新记录的时间。

    db.Save(&user) // `UpdatedAt`将会是当前时间
    
    db.Model(&user).Update("name", "jinzhu") // `UpdatedAt`将会是当前时间
    

    DeletedAt

    如果模型有DeletedAt字段,调用Delete删除该记录时,

    七、字段默认值

    package main
    
    import (
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    )
    
    type Users struct {
    	gorm.Model
    	Name string `gorm:"type:varchar(100);comment:姓名"`
    	Age  int64
    	// 设置默认值
    }
    
    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(&Users{})
    	db.Debug().Create(&Users{Name: "randy", Age: 18})
    	db.Debug().Create(&Users{Age: 18}) // 添加记录name默认为Null
    
    }
    
    

    image-20211120201553441

    为model设置默认值

    package main
    
    import (
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    )
    
    type Users struct {
    	gorm.Model
    	Name string `gorm:"type:varchar(100);default:RandySun;comment:姓名"`
    	Age  int64
    	// 设置默认值
    }
    
    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(&Users{})
    	db.Debug().Create(&Users{Age: 19}) // 添加记录name默认为Null
    }
    
    

    image-20211120214853188

    image-20211120201607641

    注意:通过tag定义字段的默认值,在创建记录时候生成的 SQL 语句会排除没有值或值为 零值 的字段。 在将记录插入到数据库后,Gorm会从数据库加载那些字段的默认值。

    举个例子:

    var Users = User{Name: "", Age: 99}
    db.Create(&user)
    

    上面代码实际执行的SQL语句是INSERT INTO users("age") values('99');,排除了零值字段Name,而在数据库中这一条数据会使用设置的默认值RandySun作为Name字段的值。

    image-20211120215532229

    注意 所有字段的零值, 比如0, "",false或者其它零值,都不会保存到数据库内,但会使用他们的默认值。 如果你想避免这种情况,可以考虑使用指针或实现 Scanner/Valuer接口,比如:

    使用指针方式实现零值存入数据库

    // 使用指针
    type Users struct {
    	gorm.Model
    	// 设置默认值
    	Name *string `gorm:"type:varchar(100);default:RandySun;comment:姓名"` 
    	Age  int64
    }
    db.Debug().Create(&Users{Name: new(string), Age: 19}) // 添加记录name默认为Null
    

    image-20211120220135920

    image-20211120220111427

    使用Scanner/Valuer接口方式实现零值存入数据库

    // 使用 Scanner/Valuer
    type Users struct {
    	gorm.Model
    	//Name *string `gorm:"type:varchar(100);default:RandySun;comment:姓名"`
    	Name sql.NullString `gorm:"type:varchar(100);default:RandySun;comment:姓名"`
    	Age  int64
    	// 设置默认值
    }
    
    db.Debug().Create(&Users{Name: sql.NullString{String: "", Valid: true}, Age: 19}) // 添加记录name默认为Null
    

    image-20211120220904299

    image-20211120220830798

    image-20211120220348541

    扩展创建选项

    例如PostgreSQL数据库中可以使用下面的方式实现合并插入, 有则更新, 无则插入。

    // 为Instert语句添加扩展SQL选项
    db.Set("gorm:insert_option", "ON CONFLICT").Create(&product)
    // INSERT INTO products (name, code) VALUES ("name", "code") ON CONFLICT;
    
  • 相关阅读:
    opencv ellipse
    Spring.NET实用技巧4——NHibernate分布式事务(下)
    Spring.NET企业架构实践之 Nhibernate + WCF + ASP.NET MVC + NVelocity 对PetShop4.0重构(二)——领域模型
    Spring.NET实用技巧3——NHibernate分布式事务(上)
    Spring.NET企业架构实践之 NHibernate + Spring.NET + WCF + Windows服务 + Silverlight 中小企业应用架构完整Demo
    关于nunit调试VS2010中的4.0程序集的问题
    Spring.NET企业架构实践之 Nhibernate + WCF + ASP.NET MVC + NVelocity 对PetShop4.0重构(三)——持久层
    Spring.NET实用技巧5——WCF环境下的NHibernate分布式事务
    有多少可爱IT精英,他们的爱情屡屡“挨踢”
    Spring.NET 1.3.1 正式版已发布
  • 原文地址:https://www.cnblogs.com/randysun/p/15626722.html
Copyright © 2011-2022 走看看