乐趣区

关于gorm:GO笔记04-GORM

GORM 介绍

gorm 是一个应用 Go 语言编写的 ORM 框架。它文档齐全,对开发者敌对,反对支流数据库。

ORM:Object Relation Mapping 对象关系映射

长处:进步开发效率

毛病:就义执行性能,灵活性

装置 GORM

go get -u github.com/jinzhu/gorm

Docker 疾速创立 Mysql 实例

在本地的 33060 端口运行一个名为 mysql,用户名 root 明码 123456 的 MySQL 容器环境:

docker run --name mysql -p 33060:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0.19
> docker exec -it mysql bash
> mysql -uroot -p
> ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY '123456';

下载 Sequel Pro Nightly(正式版连贯 Mysql8.0 会解体)

https://sequelpro.com/test-bu…

创立数据库db_test

CREATE DATABASE db_test;

连贯 Mysql

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

func main() {dsn := "root:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

GORM 操作 Mysql

type User struct {
    ID   uint
    Name string
}

func main() {dsn := "root:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local"
    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    // 主动建表
    db.AutoMigrate(&User{})
    // 创立数据
    u1 := User{1, "AAA"}
    u2 := User{2, "BBB"}
    db.Create(&u1)
    db.Create(&u2)
    // 删除
    db.Delete(&u2)
    // 更新
    db.Model(&u1).Update("name", "AAA1")
    // 查问单条
    var u User
    db.First(&u)
    fmt.Printf("%#v\n", u) //{ID:0x1, Name:"AAA1"}
    // 查问多条
    var us []User
    db.Find(&us)
    fmt.Printf("%#v\n", us)
}

GORM Model

gorm.Model

GORM 内置了一个 gorm.Model 构造体。蕴含 ID, CreatedAt, UpdatedAt, DeletedAt 四个字段。

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
}

自定义模型

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

模型定义示例

type User struct {
  gorm.Model
  Name         string
  Age          sql.NullInt64 // 零值类型
  Birthday     *time.Time `gorm:"colume:birth"` // 自定义字段名
  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:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local"
    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
        NamingStrategy: schema.NamingStrategy{
            TablePrefix:   "t_", // 表名前缀,`User` 的表名应该是 `t_users`
            SingularTable: true, // 应用复数表名,启用该选项,此时,`User` 的表名应该是 `t_user`
        },
    })
    db.AutoMigrate(&User{})// 创立 t_user 表
}

应用 TableName()重写表名

type User struct {
    gorm.Model
    Name string
}

// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {return "profiles"}

长期指定表名

// 依据 User 的字段创立 `deleted_users` 表
db.Table("deleted_users").AutoMigrate(&User{})

var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
//// SELECT * FROM deleted_users;
列名

构造体 tag 指定列名

type Animal struct {
  AnimalId    int64     `gorm:"column:beast_id"`         
  Birthday    time.Time `gorm:"column:day_of_the_beast"`
  Age         int64     `gorm:"column:age_of_the_beast"`
}
工夫戳

CreatedAt

// 能够应用 `Update` 办法来扭转 `CreateAt` 的值
db.Model(&user).Update("CreatedAt", time.Now())

UpdatedAt

db.Save(&user) // `UpdatedAt` 将会是以后工夫
db.Model(&user).Update("name", "jinzhu") // `UpdatedAt` 将会是以后工夫

DeletedAt

如果模型有 DeletedAt 字段,调用 Delete 删除该记录时,将会设置 DeletedAt 字段为以后工夫,而不是间接将记录从数据库中删除。

字段默认值

type User struct {
    ID   int64
    Name string `gorm:"default:'XXX'"`
    Age  int64
}

func main() {dsn := "root:123456@tcp(127.0.0.1:33060)/db_test?charset=utf8mb4&parseTime=True&loc=Local"
    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    db.AutoMigrate(&User{})
    u1 := User{Age: 1}
    db.Debug().Create(&u1)//INSERT INTO `users` (`name`,`age`) VALUES ('XXX',1)
    u2 := User{Name:"", Age: 2}
    db.Debug().Create(&u2)//INSERT INTO `users` (`name`,`age`) VALUES ('XXX',1)
}

留神:通过 tag 创立的默认值,创立记录时 SQL 会排除没有值或值为 零值 (0,””,false 等) 的字段。

写入零值

  1. 通过指针

    type User struct {
        ID   int64
        Name *string `gorm:"default:'XXX'"`
        Age  int64
    }
    
    func main() {
        ...
        u1 := User{Name: new(string), Age: 1}
        db.Debug().Create(&u1) //INSERT INTO `users` (`name`,`age`) VALUES ('',1)
    }
  2. 实现 Scanner/Valuer 接口

    type User struct {
        ID   int64
        Name sql.NullString `gorm:"default:'XXX'"`
        Age  int64
    }
    
    func main() {
        ...
        u1 := User{Name: sql.NullString{String: "", Valid: true}, Age: 1}
        db.Debug().Create(&u1) //INSERT INTO `users` (`name`,`age`) VALUES ('',1)
    }

CRUD

创立

应用 db.NewRecord() 查问主键是否存在,主键为空应用 db.Create() 创立记录

result := db.Create(&user)
fmt.Println("ID:", user.ID)               // 插入主键 ID
fmt.Println("ERROR:", result.Error)       // 谬误
fmt.Println("ROWS:", result.RowsAffected) // 影响行数

查问

个别查问
var user User
// 获取第一条记录(主键升序)db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// 随机获取一条记录
db.Take(&user)
//SELECT * FROM users LIMIT 1;

// 获取最初一条记录(主键降序)db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

// 获取全副记录
db.Find(&user)
//SELECT * FROM `users`
Where 条件查问
// 条件查问单条记录
db.Where("name=?", "AAA").First(&user)
//SELECT * FROM `users` WHERE name='AAA' ORDER BY `users`.`id` LIMIT 1

// 条件查问多条记录
db.Where("name=?", "AAA").Find(&user)
//SELECT * FROM `users` WHERE name='AAA'

// 不等于
db.Where("name <> ?", "AAA").Find(&user)
//SELECT * FROM `users` WHERE name<>'AAA'

//IN
db.Where("name IN (?)", []string{"AAA", "BBB"}).Find(&user)
//SELECT * FROM `users` WHERE name IN ('AAA','BBB')

//AND
db.Debug().Where("name=? AND age=?", "AAA", 11).Find(&user)
//SELECT * FROM `users` WHERE name='AAA' AND age=11
Struct & Map 条件查问
//Struct
db.Where(&User{Name: "AAA"}).Find(&user)
//SELECT * FROM `users` WHERE `users`.`name` = 'AAA'
//* 当应用构造作为条件查问时,GORM 只会查问非零值字段。//Map
db.Where(map[string]interface{}{"name": "AAA"}).Find(&user)
//SELECT * FROM `users` WHERE `name` = 'AAA'
// 如果想要蕴含零值查问条件,你能够应用 map,其会蕴含所有 key-value 的查问条件

// 主键切片
db.Where([]int64{1, 2, 3}).Find(&user)
//SELECT * FROM `users` WHERE `users`.`id` IN (1,2,3)
内联条件

查问条件也能够被内联到 FirstFind 之类的办法中,其用法相似于 Where

db.First(&user, "id = ?", 1)
Not 条件
db.Not("ID=?", 1).First(&user)
//SELECT * FROM `users` WHERE NOT ID=1 ORDER BY `users`.`id` LIMIT 1
Or 条件
db.Where("ID=?", 1).Or("ID=?", 2).First(&user)
//SELECT * FROM `users` WHERE ID=1 OR ID=2 ORDER BY `users`.`id` LIMIT 1
抉择特定字段
db.Select("name", "age").First(&user)
//SELECT `name`,`age` FROM `users` ORDER BY `users`.`id` LIMIT 1
排序
db.Order("age desc").Order("name asc").Find(&user)
//SELECT * FROM `users` ORDER BY age desc,name asc
分组 Group By & Having
db.Group("name").Having("name=?", "AAA").Find(&user)
Joins
db.Debug().Joins("left join emails on emails.uid = users.id").Find(&user)
//SELECT * FROM `users` left join emails on emails.uid = users.id
Pluck

查问 model 中的一个列作为切片(多列用 Scan)

var ages []int64
db.Debug().Model(&User{}).Pluck("age", &ages)
//SELECT `age` FROM `users`

db.Debug().Model(&User{}).Select("name", "age").Scan(&user)
//SELECT `name`,`age` FROM `users`
立刻执行办法
Create`, `First`, `Find`, `Take`, `Save`, `UpdateXXX`, `Delete`, `Scan`, `Row`, `Rows

应用多个立刻执行办法时,后一个立刻执行办法会复用前一个立刻执行办法的条件 (不包含内联条件)。

db.Find(&user).Count(&count)

//SELECT * FROM `users`
//SELECT count(*) FROM `users`

留神 Count 必须是链式查问的最初一个操作,因为它会笼罩后面的 SELECT

更新

更新所有字段

Save()默认会更新该对象的所有字段,即便你没有赋值。

var user User
user.Name = "SSS"
db.Debug().Save(&user)
//INSERT INTO `users` (`name`,`age`) VALUES ('SSS',0)
更新指定字段

更新单个列:Update

当应用 Update 更新单个列时,你须要指定条件,否则会返回 ErrMissingWhereClause 谬误。

// 更新单个属性,如果它有变动
db.Model(&user).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

// 依据给定的条件更新单个属性
db.Model(&user).Where("active = ?", true).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

更新多个列:Updates

Updates 办法反对 structmap[string]interface{} 参数。当应用 struct 更新时,默认状况下,GORM 只会更新非零值的字段

// 应用 map 更新多个属性,只会更新其中有变动的属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
//// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// 应用 struct 更新多个属性,只会更新其中有变动且为非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;

// 正告:当应用 struct 更新时,GORM 只会更新那些非零值的字段
// 对于上面的操作,不会产生任何更新,"", 0, false 都是其类型的零值
db.Model(&user).Updates(User{Name: "", Age: 0, Active: false})
更新选定字段

更新时选定字段:Select

更新时疏忽字段:Omit

// 应用 Map 进行 Select
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;

db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// 应用 Struct 进行 Select(会 select 零值的字段)db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;

// Select 所有字段(查问包含零值字段的所有字段)db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

// Select 除 Role 外的所有字段(包含零值字段的所有字段)db.Model(&user).Select("*").Omit("Role").Update(User{Name: "jinzhu", Role: "admin", Age: 0})
应用 SQL 表达式更新
db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))

删除

// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;

// 带额定条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";

软删除

// user's ID is `111`
db.Delete(&user)
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

// Batch Delete
db.Where("age = ?", 20).Delete(&User{})
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// Soft deleted records will be ignored when querying
db.Where("age = 20").Find(&user)
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;

db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;

参考资料:

GORM 中文站:https://gorm.io/zh_CN/

GORM 入门指南:https://www.liwenzhou.com/pos…

GORM CRUD:https://www.liwenzhou.com/pos…

退出移动版