文档
https://learnku.com/docs/gorm/v2/v2_release_note/9756
https://gorm.cn/zh_CN/docs/下载安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql简单示例
package main
import (
    "fmt"
    "time"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)
// 模型定义
type Product struct {
    ID       uint
    Code     string
    Price    uint
    CreateAt time.Time
    UpdateAt time.Time
}
func main(){
    // gorm数据库提前创建好
    dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
    // 创建连接
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Silent),
    })
    if err != nil{
        fmt.Printf("conncetion failed, err:%v", err)
    }
    // 迁移创建表,默认创建到数据库的表名为结构体名+s
    db.AutoMigrate(&Product{})
    // 创建一条数据
    product := Product{
        ID:       1,
        Code:     "10086",
        Price:    120,
        CreateAt: time.Now(),
        UpdateAt: time.Now(),
    }
    db.Create(&product)
    
    // 读
    var product Product
    db.First(&product, 1) // 根据整形主键查找
    db.First(&product, "code = ?", "D42") // 查找code字段值为D42
    
    // 更新单条
    db.Model(&product).Update("Price", 200) //将表products的price字段的值更新为200
    // 更新多个字段
    db.Model(&product).Updates(Product{Price:200, Code:"F42"})// 仅更新非零值字段
    // 使用map类型更新
    db.Model(&product).Updates(map[string]interfaces{}{
        "Price": 200,
        "Code": "F42"
    })
    
    
    // 删除
    db.Delete(&product, 1) // 根据主键删除
    
}声明模型
// 模型定义
type User struct{
    ID  uint
    Name  string
    Email *string
    Age uint8
    Birthday  *time.Time
    MemberNumber  sql.NullString
    ActivedAt sql.NullTime
    CreateAt  time.Time
    UpdateAt  time.Time
}
// gorm内部自己的结构体gorm.Model,可以直接嵌套到自己的结构体中
type Model struct{
    ID  uint `gorm:"primaryKey"`
    CreateAt time.Time
    UpdateAt time.Time
    DeleteAt gorm.DeleteAt `gorm:"index"`
}
// 创建/更新时间
type User struct {
  CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
  UpdatedAt int       // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
}
// 正常结构体字段通过标签嵌入
type Author struct{
    Name string
    Email string
}
type Blog struct{
    ID int
    Author Author `gorm:"embedded"
    Upvotes int32`
}
// 通过标签来为db中的字段名添加前缀
type Blog struct{
  ID      int
  Author  Author `gorm:"embedded;embeddedPrefix:author_"`
  Upvotes int32
}
// 等效于
type Blog struct {
  ID      int
  Author  Author `gorm:"embedded;embeddedPrefix:author_"`
  Upvotes int32
}
// 等效于
type Blog struct {
  ID          int64
  AuthorName  string
  AuthorEmail string
  Upvotes     int32
}修改表名
package main
import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)
type Student struct{ // 对应到 students这个表名
    Id int   // id
    Name String // name
    // Score float32 // score
    // tag方式
    Rank float32 `gorm:"column:score"` //score
    // UpdateAt time.Time // update_at
}
// 修改表名 默认表名是结构体名小写+s
func (Student) TableName() string{
    return "student"
}字段标签
| 标签名 | 说明 | 
| column | 指定 db 列名 | 
| type | 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如: | 
| size | 指定列大小,例如: | 
| primaryKey | 指定列为主键 | 
| unique | 指定列为唯一 | 
| default | 指定列的默认值 | 
| precision | 指定列的精度 | 
| scale | 指定列大小 | 
| not null | 指定列为 NOT NULL | 
| autoIncrement | 指定列为自动增长 | 
| embedded | 嵌套字段 | 
| embeddedPrefix | 嵌入字段的列名前缀 | 
| autoCreateTime | 创建时追踪当前时间,对于  | 
| autoUpdateTime | 创建/更新时追踪当前时间,对于  | 
| index | 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情 | 
| uniqueIndex | 与  | 
| check | 创建检查约束,例如  | 
| <- | 设置字段写入的权限,  | 
| -> | 设置字段读的权限, | 
| - | 忽略该字段, | 
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 版本自动配置
}), &grom.Config{})gorm 增删改查
gorm 增
// 结构体定义
type User struct{
    ID  uint
    Name    string
    Email   *string
    Age uint8
    Birthday    *time.Time
    MemberNumber    sql.NullString
    ActivedAt   sql.NullTime
    CreateAt    time.Time
    UpdateAt    time.Time
}
// helper function
func timePtr(t time.Time) *time.Time{
    return &t
}
// 创建一条数据
// Birthday为指针类型,需要创建一个helper函数来返回time.Time的地址,不能直接使用&来直接取地址
user := User{Name: "jinzhu", Age: 18, Birthday: timePtr(time.Now())}
result := db.Create(&user)
fmt.Println(user.ID)             // 返回数据的主键
fmt.Println(result.RowsAffected) // 插入记录的条数
// 选定字段创建
user := User{Name: "liuwei", Age: 28, Birthday: timePtr(time.Now()), CreateAt: time.Now()}
db.Select("Name", "Age", "CreateAt").Create(&user)
// Omit排除选定字段
user := User{Name: "jack", Age: 28, Birthday: timePtr(time.Now()), CreateAt: time.Now()}
db.Omit("Age", "CreateAt").Create(&user)
// 钩子,创建记录时会自动调用这些方法
// BeforeSave/BeforeCreate, AfterSave/AfterCreate
func (u *User) BeforeCreate(tx *gorm.DB)(err error){
    fmt.Println("创建之前调用...")
    return nil
}
user := User{Name: "JOJO", Age: 24, Birthday: timePtr(time.Now()), CreatedAt: time.Now()}
db.Create(&user)
// 批量插入
var users = []User{{Name: "qiqi1"}, {Name: "qiqi2"}, {Name: "qiqi3"}}
db.Create(&users)
for _, user := range users {
    fmt.Println(user.ID)
}
// 使用map创建
db.Model(&User{}).Create(map[string]interface{}{
    "Name":"wanglei",
    "Age": 25,
})
// 批量map插入
db.Model(&User{}).Create([]map[string]interface{}{
    {"Name":"xiao1", "Age": 22},
    {"Name":"xiao2", "Age": 23},
    {"Name":"xiao3", "Age": 24},
})
// 关联创建(一对一),外键会自动关联
db.Create(&User{
    Name: "liuwei",
    CreditCard: CreditCard{Number: "12345678"}
})
// 跳过关联创建数据
user := User{Name:"wanglei", CreditCard: CreditCard{Number: "11111111"}}
db.Omit("CreditCard").Create(&user)
// 跳过所有关联clause.Associations
db.Omit(clause.Associations).Create(&user)
// uuid
go get -u github.com/satori/go.uuid
// 简单使用
u, _ := uuid.NewV4()  //uuid.UUID类型,NewV4()每次返回不同uuid
// 默认值使用标签default
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    u.UUID = uuid.NewV4().String()
    return nil
}
type User struct {
    ID   int64
    Name string `gorm:"default: galeone"`
    Age  int64  `gorm:"default:18"`
    UUID string
}
func main(){
    // 默认会自动创建uuid
    db.Create(&User{Name: "liuwei1", Age: 28})
    db.Create(&User{Name: "liuwei2", Age: 29})
}
// 默认值零值问题
// 当Age传入0时,存入到数据时会被识别为default,即使用default的值18
db.Create(&User{Name: "liuwei3", Age: 0})
// 零值问题解决
// 将Age的类型int64改为指针类型*int64
type User struct {
    ID   int64
    Name string `gorm:"default: galeone"`
    Age  *int64  `gorm:"default:18"`
    UUID string
}gorm 查
var user User
//获取第一条记录(默认按主键升序)
db.First(&user)
fmt.Println(user.ID)
fmt.Println(user.Name)
// 获取第一条记录,没有指定排序字段
db.Take(&user)
fmt.Println(user.ID)
fmt.Println(user.Name)
// 获取最后一条记录
db.Last(&user)
//如果struct没有显示的定义主键,则按第一个字段排序
type Language struct {
  Code string
  Name string
}
db.First(&Language{})
// 主键检索
// 只支持整形,字符串会导致sql注入问题
// 查询id=10的数据
db.First(&user, 10)
// 查询id在1,2,3中的
db.Find(&user, []int{1,2,3})
// 检索对象
// 获取全部数据,传入对象
db.Find(&user)
//获取第一条匹配的记录
db.Where("name = ?", "jinzhu").First(&user)
// 获取全部匹配记录
db.Where("name <> ?", "jinzhu").Find(&user)
// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&user)
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&user)
//AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&user)
// Time
db.Where("updated_at > ?", lastWeek).Find(&user)
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&user)
// struct类型查询
// 当使用结构作为条件查询时,GORM 只会查询非零值字段
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// Age不会作为查询条件
db.Where(&User{Name: "jinzhu", Age: 0}).First(&user)
// Map类型查询,可以使用零值作为查询条件
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&user)
// 主键切片
db.Where([]int64{20,21,22}).Find(&user)
// 内联条件,用法与where相同
db.Find(&user, "name = ?", "jinzhu")
// Not条件
db.Not("name  = ?", "jinzhu").First(&user)
// Or条件
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&user)
// 查询指定字段
db.Select("name", "age").Find(&user)
// COALESCE()函数返回传入的参数中第一个非null的值
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;
// Order排序
db.Order("age desc, name").Find(&user)
// 指定多个字段排序
db.Order("id desc").Order("age asc").Find(&users)
//Limit限制返回的数量
db.Limit(3).Find(&user)
// Offset跳过指定的数量返回
db.Offset(3).Find(&user)
// Group分组 Having分组后过滤
type result struct{
    Date time.Time
    Total int
}
db.Model(&User{}).Select("name,sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name`
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
  ...
}
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {
  ...
}
type Result struct {
  Date  time.Time
  Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
// Distinct 去重
db.Distinct("name", "age").Order("name, age desc").Find(&result)
// Joins,左查询,右查询,内联查询
type result struct{
    Name string
    Email string
}
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
  ...
}
db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
// 带参数的多表连接
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
// Joins预加载,查询本表的同时将相关的外键表也同时查询出来
db.Joins("Company").Find(&users)
// Scan结果至struct,用法于Find类似
type Result struct{
    Name string
    Age int
}
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "auto").Scan(&result)
// 原生sql Raw
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)gorm 更新
// Save保存
type User struct{
    ID int64
    Name string
    Age int64
    Birthday time.Time
    UpdateAt time.Time
}
var user User
db.First(&user)
user.Name = "jinzhu666"
user.Age = 100
db.Save(&user)
// 更新单个列,必须指定条件,且该对象主键有值
// 当active=true时,更新name的值为hello
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// 更新多列
// 根据struct更新,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 13, Active: false})
// 根据map更新属性
db.Model(&user).Updates(map[string]interfaces{}{
    "name": "hello",
    "age": 18,
    "actived": false
})
// 更新钩子
// 更新记录时会被自动调用
func (u *User) BeforeUpdate(tx *gorm.DB)(err error){
    if u.Role == "admin"{
        return errors.New("admin user not allowed to update")
    }
    return
}
// 批量更新Updates
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// 使用sql表达式更新
// product 的 ID 是 `3`
DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
// UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;
// 在更新时修改值
若要在 Before 钩子中改变要更新的值,如果它是一个完整的更新,可以使用 Save;否则,应该使用 scope.SetColumn ,例如:
func (user *User) BeforeSave(scope *gorm.Scope) (err error) {
  if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil {
    scope.SetColumn("EncryptedPassword", pw)
  }
}
db.Model(&user).Update("Name", "jinzhu")gorm 删除
// 删除一条记录,删除对象需要指定主键,否则会触发批量删除
var email Email
email.Id = 10
db.Delete(&email)
// 带条件删除
db.Where("name = ?", "jinzhu").Delete(&email)
// 根据主键删除
db.Delete(&User{}, 10)
var user User
db.Delete(&user, []int{1,2,3})
// 删除钩子
func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
    if u.Role == "admin" {
        return errors.New("admin user not allowed to delete")
    }
    return
}
// 批量删除,必须加条件
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")创建表,一对一/一对多/多对多关联表
// 一对一
type CreditCard struct {
  gorm.Model
  Number string
  UserID uint
}
type User struct {
  gorm.Model
  Name       string
  CreditCard CreditCard
}
func main() {
  dsn := "root:123456@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    fmt.Printf("connection failed, err:%v", err)
  }
  // 注意一下结构体的顺序
  db.AutoMigrate(&User{}, &CreditCard{})
}
// 一对多 User 有多张 CreditCard,UserID 是外键
type User struct {
  gorm.Model
  CreditCards []CreditCard // 切片
}
type CreditCard struct {
  gorm.Model
  Number string
  UserID uint
}
// 对多对
// 会在两个 model 中添加一张连接表,使用AutoMigrate创建表时,会自动创建第三表
// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
  gorm.Model
  Name string
}
// 多对多,自定义第三张连接表
type Person struct {
  ID        int
  Name      string
  Addresses []Address `gorm:"many2many:person_addresses;"`
}
type Address struct {
  ID   uint
  Name string
}
type PersonAddress struct {
  PersonID  int
  AddressID int
  CreatedAt time.Time
  DeletedAt gorm.DeletedAt
}
func (PersonAddress) BeforeCreate(db *gorm.DB) error {
  // ...
}
// 修改 Person 的 Addresses 字段的连接表为 PersonAddress
// PersonAddress 必须定义好所需的外键,否则会报错
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})重写外键名
//默认的外键使用拥有者的类型名加上主字段名
//如下:其外键一般是 CompanyID
type User struct {
  gorm.Model
  Name         string
  CompanyRefer int
  Company      Company `gorm:"foreignKey:CompanyRefer"`
  // 使用 CompanyRefer 作为外键
}
type Company struct {
  ID   int
  Name string
}更改外键
// 默认外键都是使用主键id作为外键
type User struct {
  gorm.Model
  Name      string
  CompanyID string
  Company   Company `gorm:"references:Code"` // 使用 Code 作为外键
}
type Company struct {
  ID   int
  Code string
  Name string
}外键约束
// 你可以通过为标签 constraint 配置 OnUpdate、OnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建
type User struct {
  gorm.Model
  Name      string
  CompanyID int
  Company   Company `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type Company struct {
  ID   int
  Name string
}反向引用(多对多)
// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
  gorm.Model
  Languages []*Language `gorm:"many2many:user_languages;"`
}
type Language struct {
  gorm.Model
  Name string
  Users []*User `gorm:"many2many:user_languages;"`
}关联标签
| 标签 | 描述 | 
| foreignKey | 指定外键 | 
| references | 指定引用 | 
| polymorphic | 指定多态类型 | 
| polymorphicValue | 指定多态值、默认表名 | 
| many2many | 指定连接表表名 | 
| joinForeignKey | 指定连接表的外键 | 
| joinReferences | 指定连接表的引用外键 | 
| constraint | 关系约束,例如: | 
预加载
// 查找一张表的时候,将另外一张表的数据也同时查询出来,用于优化查询
type User struct {
  gorm.Model
  Username string
  Orders   []Order
}
type Order struct {
  gorm.Model
  UserID uint
  Price  float64
}
// 查找 user 时预加载相关 Order
db.Preload("Orders").Find(&users)
db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
// Joins预加载,多用于一对一关系, inner join加载
db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
// 预加载全部关联clause.Associations
type User struct {
  gorm.Model
  Name       string
  CompanyID  uint
  Company    Company
  Role       Role
}
db.Preload(clause.Associations).Find(&users)    
-------------------------------------------
个性签名:代码过万,键盘敲烂!!!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
    
    
    










