gorm
官方文档:https://gorm.io/zh_CN/docs
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")
  }
  // 迁移 schema
  db.AutoMigrate(&Product{})
  // Create
  db.Create(&Product{Code: "D42", Price: 100})
  // Read
  var product Product
  db.First(&product, 1) // 根据整型主键查找
  db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录
  // Update - 将 product 的 price 更新为 200
  db.Model(&product).Update("Price", 200)
  // Update - 更新多个字段
  db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
  db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
  // Delete - 删除 product
  db.Delete(&product, 1)
}
若要打开 MySQL
package main
import (
    "fmt"
    "time"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)
// 定义一个数据模型(user表)
// 列名是字段名的蛇形小写(PassWd->pass_word)
type User struct {
    Id          uint        `gorm:"AUTO_INCREMENT"`
    Name        string      `gorm:"size:50"`
    Age         int         `gorm:"size:3"`
    Birthday    *time.Time
    Email       string      `gorm:"type:varchar(50);unique_index"`
    PassWord    string      `gorm:"type:varchar(25)"`
}
var db *gorm.DB
func main() {
    db,err := gorm.Open("mysql","root:bgbiao.top@(127.0.0.1:13306)/test_api?charset=utf8&parseTime=True&loc=Local")
    if err != nil {
        fmt.Errorf("创建数据库连接失败:%v",err)
    }
    // defer db.Close() // GORM v2 不需要此步
    // 自动迁移数据结构(table schema)
    // 注意:在gorm中,默认的表名都是结构体名称的复数形式,比如User结构体默认创建的表为users
    // db.SingularTable(true) 可以取消表名的复数形式,使得表名和结构体名称一致
    db.AutoMigrate(&User{})
    // 添加唯一索引
    db.Model(&User{}).AddUniqueIndex("name_email", "id", "name","email")
    // 插入记录
    db.Create(&User{Name:"bgbiao",Age:18,Email:"bgbiao@bgbiao.top"})
    db.Create(&User{Name:"xxb",Age:18,Email:"xxb@bgbiao.top"})
    var user User
    var users []User
    // 查看插入后的全部元素
    fmt.Printf("插入后元素:\n")
    db.Find(&users)
    fmt.Println(users)
    // 查询一条记录
    db.First(&user,"name = ?","bgbiao")
    fmt.Println("查看查询记录:",user)
    // 更新记录(基于查出来的数据进行更新)
    db.Model(&user).Update("name","biaoge")
    fmt.Println("更新后的记录:",user)
    // 删除记录
    db.Delete(&user)
    // 查看全部记录
    fmt.Println("查看全部记录:")
    db.Find(&users)
    fmt.Println(users)
}
# 运行gorm实例
$ go run gorm-mysql-example.go
插入后元素:
[{1 bgbiao 18 <nil> bgbiao@bgbiao.top } {2 xxb 18 <nil> xxb@bgbiao.top }]
查看查询记录: {1 bgbiao 18 <nil> bgbiao@bgbiao.top }
更新后的记录: {1 biaoge 18 <nil> bgbiao@bgbiao.top }
查看全部记录:
[{2 xxb 18 <nil> xxb@bgbiao.top }]
快速开始
https://zhuanlan.zhihu.com/p/113251066
安装
支持的数据库以及导入路径如下:
- mysql: github.com/jinzhu/gorm/dialects/mysql
 - postgres: github.com/jinzhu/gorm/dialects/postgres
 - sqlite: github.com/jinzhu/gorm/dialects/sqlite
 - sqlserver: github.com/jinzhu/gorm/dialects/mssql
 
gorm 框架只是简单封装了数据库的驱动包,在安装时仍需要下载原始的驱动包
$ go get -u github.com/jinzhu/gorm
$ go get -u github.com/go-sql-driver/mysql
$ go get -u gorm.io/driver/mysql
模型
模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成
type User struct {
  ID           uint
  Name         string
  Email        *string
  Age          uint8
  Birthday     *time.Time
  MemberNumber sql.NullString
  ActivatedAt  sql.NullTime
  CreatedAt    time.Time
  UpdatedAt    time.Time
}
约定
GORM 倾向于约定,而不是配置。
默认情况下,Gorm:
- 使用 
ID作为主键 - 使用结构体名的 
蛇形复数作为表名 - 字段名的 
蛇形作为列名 - 使用 
CreatedAt、UpdatedAt字段追踪创建、更新时间 
遵循 GORM 已有的约定,可以减少您的配置和代码量。如果约定不符合您的需求,GORM 允许您自定义配置它们
gorm.Model
GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt
// gorm.Model 的定义
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}
您可以将它嵌入到您的结构体中,以包含这几个字段
字段标签
声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格
| 标签名 | 说明 | 
|---|---|
| column | 指定 db 列名 | 
| type | 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null、size, 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 | 迁移时为字段添加注释 | 
字段级权限控制
可导出的字段在使用 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 读写会忽略该字段
}
创建/更新时间追踪(纳秒、毫秒、秒、Time)
GORM 约定使用 CreatedAt、UpdatedAt 追踪创建/更新时间。如果您定义了这种字段,GORM 在创建、更新时会自动填充 当前时间
要使用不同名称的字段,您可以配置 autoCreateTime、autoUpdateTime 标签
如果您想要保存 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
}
嵌入结构体
对于匿名字段,GORM 会将其字段包含在父结构体中,例如:
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
}
对于正常的结构体字段,你也可以通过标签 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 允许通过标签为关联配置外键、约束、many2many 表
| 标签 | 描述 | 
|---|---|
| foreignKey | 指定当前模型的列作为连接表的外键 | 
| references | 指定引用表的列名,其将被映射为连接表外键 | 
| polymorphic | 指定多态类型,比如模型名 | 
| polymorphicValue | 指定多态值、默认表名 | 
| many2many | 指定连接表表名 | 
| joinForeignKey | 指定连接表的外键列名,其将被映射到当前表 | 
| joinReferences | 指定连接表的外键列名,其将被映射到引用表 | 
| constraint | 关系约束,例如:OnUpdate、OnDelete | 
连接到数据库
GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
MySQL
import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
func main() {
  // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
  dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
MySQl 驱动程序提供了 一些高级配置 可以在初始化过程中使用,例如:
db, err := gorm.Open(mysql.New(mysql.Config{
  DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
  DefaultStringSize: 256, // string 类型字段的默认长度
  DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
  DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
  DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
  SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
自定义驱动
GORM 允许通过 DriverName 选项自定义 MySQL 驱动,例如:
import (
  _ "example.com/my_mysql_driver"
  "gorm.io/gorm"
)
db, err := gorm.Open(mysql.New(mysql.Config{
  DriverName: "my_mysql_driver",
  DSN: "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local", // Data Source Name,参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name
}), &gorm.Config{})
现有的数据库连接
GORM 允许通过一个现有的数据库连接来初始化 *gorm.DB
import (
  "database/sql"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
sqlDB, err := sql.Open("mysql", "mydb_dsn")
gormDB, err := gorm.Open(mysql.New(mysql.Config{
  Conn: sqlDB,
}), &gorm.Config{})
SQLite
import (
  "gorm.io/driver/sqlite"
  "gorm.io/gorm"
)
// github.com/mattn/go-sqlite3
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
注意: 您也可以使用
file::memory:?cache=shared替代文件路径。 这会告诉 SQLite 在系统内存中使用一个临时数据库。
连接池
GORM 使用 database/sql 维护连接池
sqlDB, err := db.DB()
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Hour)
创建
创建记录
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 通过数据的指针来创建
user.ID             // 返回插入数据的主键
result.Error        // 返回 error
result.RowsAffected // 返回插入记录的条数
筛选指定字段来创建记录
创建记录并更新给出的字段。
db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")
创建一个记录且一同忽略传递给略去的字段值。
db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")
批量插入
要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条 SQL 语句来插入所有数据,并回填主键的值,钩子方法也会被调用。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
  user.ID // 1,2,3
}
使用 CreateInBatches 分批创建时,你可以指定每批的数量,例如:
var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// 数量为 100
db.CreateInBatches(users, 100)
Upsert 和 Create 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 创建记录时将调用这些钩子方法
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
  	u.UUID = uuid.New()
    if u.Role == "admin" {
        return errors.New("invalid role")
    }
    return
}
如果您想跳过 钩子 方法,您可以使用 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{}{} 创建记录,例如:
db.Model(&User{}).Create(map[string]interface{}{
  "Name": "jinzhu", "Age": 18,
})
// batch insert from `[]map[string]interface{}{}`
db.Model(&User{}).Create([]map[string]interface{}{
  {"Name": "jinzhu_1", "Age": 18},
  {"Name": "jinzhu_2", "Age": 20},
})
注意: 根据 map 创建记录时,association 不会被调用,且主键也不会自动填充