• Gorm—Go语言数据库框架



    相关链接:
    GORM中文官网: https://gorm.io/zh_CN/docs/
    GORM入门: https://www.liwenzhou.com/posts/Go/gorm/
    GORM增删改查CRUD: https://www.liwenzhou.com/posts/Go/gorm_crud/

    一、GORM简介

    1、什么是Gorm?

    The fantastic ORM library for Golang aims to be developer friendly. ——官网

    2、为什么选择GORM?

    • 全功能 ORM
    • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
    • Create,Save,Update,Delete,Find 中钩子方法
    • 支持 PreloadJoins 的预加载
    • 事务,嵌套事务,Save Point,Rollback To Saved Point
    • Context、预编译模式、DryRun 模式
    • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
    • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
    • 复合主键,索引,约束
    • Auto Migration
    • 自定义 Logger
    • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
    • 每个特性都经过了测试的重重考验
    • 开发者友好

    3、如何使用GORM?

    • 安装【GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server 】
    ## 必须安装gorm
    go get -u gorm.io/gorm     
    ## 安装相应的数据库驱动。GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
    go get -u gorm.io/driver/mysql  
    
    • 1
    • 2
    • 3
    • 4
    • 连接到MySQL数据库
    package main
    
    import (
    	"fmt"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"time"
    )
    
    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{})
    	db, err := gorm.Open(mysql.New(mysql.Config{
    		DSN:                       "root:123456@tcp(192.168.168.101: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{})
    	if err != nil {
    		panic("failed to connect database")
    	}
    	// ----------------------------数据库连接池----------------------------
    	sqlDB, err := db.DB()
    	// SetMaxIdleConns 设置空闲连接池中连接的最大数量
    	sqlDB.SetMaxIdleConns(10)
    	// SetMaxOpenConns 设置打开数据库连接的最大数量。
    	sqlDB.SetMaxOpenConns(100)
    	// SetConnMaxLifetime 设置了连接可复用的最大时间。
    	sqlDB.SetConnMaxLifetime(time.Hour)
    	fmt.Println("success to link mysql")
    	select {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    4、约定大于配置大于编码

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

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

    GORM 默认使用ID作为表的主键

    `type User struct { ID  string // 默认情况下,名为 `ID` 的字段会作为表的主键 Name string}`
    
    • 1

    GORM 默认使用结构体名的 snake case 复数作为表名

    eg:结构体 userinfo,其表名默认为user_infos

    蛇形命名法(snake case)类似 驼峰命名法(camel case) ,都是要**克服单词间的空格,从而把不同单词串连起来,**最终达到创造出一种新的"单词"的效果。

    临时指定表名

    // 根据 User 的字段创建 `deleted_users` 表
    db.Table("deleted_users").AutoMigrate(&User{})
    
    // 从另一张表查询数据
    var deletedUsers []User
    db.Table("deleted_users").Find(&deletedUsers)
    // SELECT * FROM deleted_users;
    
    db.Table("deleted_users").Where("name = ?", "jinzhu").Delete(&User{})
    // DELETE FROM deleted_users WHERE name = 'jinzhu';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    gorm.Model

    GORM 定义一个 gorm.Model 结构体,其包括字段 IDCreatedAtUpdatedAtDeletedAt

    // gorm.Model 的定义
    type Model struct {
      ID        uint           `gorm:"primaryKey"`
      CreatedAt time.Time
      UpdatedAt time.Time
      DeletedAt gorm.DeletedAt `gorm:"index"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    tag标签【重点】

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

    标签名说明
    column指定 db 表列名
    type列数据类型,推荐使用兼容性好的通用类型
    serializer指定如何序列化/反序列化到数据库, e.g: serializer:json/gob/unixtime
    size指定列的数据大小/长度, e.g: size:256
    primaryKey指定列作为主键
    unique指定列唯一
    default指定列为默认值
    precision指定列的精度
    scalespecifies column scale
    not null指定列 NOT NULL
    autoIncrement指定列为自增列
    autoIncrementIncrementauto increment step, controls the interval between successive column values
    embedded嵌入字段
    embeddedPrefix嵌入字段的列名前缀
    autoCreateTime记录创建时间
    autoUpdateTime记录创建/更新时间
    index根据选项创建索引
    uniqueIndex唯一索引
    check创建检查约束, eg: check:age > 13, refer Constraints
    <-设置字段的写权限
    <-:create create-only field,
    <-:update update-only field,
    <-:false no write permission,
    <- create and update permission
    ->设置字段读权限
    ->:false no read permission
    -忽略当前字段
    - no read/write permission,
    -:migration no migrate permission,
    -:all no read/write/migrate permission
    comment【迁移时】为字段添加注释

    高级选项

    字段权限

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

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    创建、更新时间

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

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

    type User struct {
      CreatedAt time.Time // 在创建时,如果该字段值为零值,则使用当前时间填充
      UpdatedAt int       // 在创建时该字段值为零值或者在更新时,使用当前时间戳秒数填充
      Updated   int64 `gorm:"autoUpdateTime:nano"` // 使用时间戳填纳秒数充更新时间
      Updated   int64 `gorm:"autoUpdateTime:milli"` // 使用时间戳毫秒数填充更新时间
      Created   int64 `gorm:"autoCreateTime"`      // 使用时间戳秒数填充创建时间
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    嵌入结构体

    可以通过标签 embedded 将一个结构体嵌入另一个结构体。可以使用标签 embeddedPrefix 来为 db 中的字段名添加前缀。

    type Author struct {
        Name  string
        Email string
    }
    
    type Blog struct {
      ID      int
      Author  Author `gorm:"embedded;embeddedPrefix:author_"`
      Upvotes int32
    }
    // 等效于
    type Blog struct {
      ID          int64
        AuthorName  string
        AuthorEmail string
      Upvotes     int32
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    二、CRUD

    Gorm增删改查CRUD最常用到的语法。

    1、增[Create]

    创建单条记录

    // -----------------------------插入单条记录---------------------------------
    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")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    创建批量记录

    // -----------------------------批量插入多条记录-----------------------------
    //要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条SQL语句来插入所有数据,并回填主键的值,钩子方法也会被调用。
    var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
    db.Create(&users)
    
    //使用 CreateInBatches 分批创建时,你可以指定每批的数量。
    var users = []User{{name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
    // 数量为 100
    db.CreateInBatches(users, 100)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    创建hook【创建钩子】

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    支持Map创建

    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},
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    默认值

    对于声明了默认值的字段,像 0''false 等零值是不会保存到数据库。需要使用指针类。

    type User struct {
      gorm.Model
      Name string         `gorm:"default:xiaoming"`
      Age  *int           `gorm:"default:18"`
      Active sql.NullBool `gorm:"default:true"`
    }
    
    type User struct {
      ID        string `gorm:"default:uuid_generate_v3()"` // db func
      FirstName string
      LastName  string
      Age       uint8
      FullName  string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、查[Retrieve]

    查询指定记录

    GORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误。

    如果想避免ErrRecordNotFound错误,可以使用Find,比如db.Limit(1).Find(&user)Find方法可以接受struct和slice的数据。

    // 获取第一条记录(主键升序)。只有在目标 struct 是指针或者通过 db.Model() 指定 model 时,该方法才有效。
    db.First(&user)
    // SELECT * FROM users ORDER BY id LIMIT 1;
    
    // 获取一条记录,没有指定排序字段
    db.Take(&user)
    // SELECT * FROM users LIMIT 1;
    
    // 获取最后一条记录(主键降序)
    db.Last(&user)
    // SELECT * FROM users ORDER BY id DESC LIMIT 1;
    
    result := db.First(&user)
    result.RowsAffected // 返回找到的记录数
    result.Error        // returns error or nil
    
    // 检查 ErrRecordNotFound 错误
    errors.Is(result.Error, gorm.ErrRecordNotFound)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    主键索引查询

    db.First(&user, 10)
    // SELECT * FROM users WHERE id = 10;
    
    db.First(&user, "10")
    // SELECT * FROM users WHERE id = 10;
    
    db.Find(&users, []int{1,2,3})
    // SELECT * FROM users WHERE id IN (1,2,3);
    
    db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
    // SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    查询全部记录

    // Get all records
    result := db.Find(&users)
    // SELECT * FROM users;
    
    result.RowsAffected // returns found records count, equals `len(users)`
    result.Error        // returns error
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查询hook【查询钩子】

    // -----------------------------代码示例-------------------------
    func (u *User) AfterFind(tx *gorm.DB) (err error) {
      if u.MemberShip == "" {
        u.MemberShip = "user"
      }
      return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    条件【重点】

    //-----------------------------string条件-------------------------
    // Get first matched record
    db.Where("name = ?", "jinzhu").First(&user)
    // SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
    
    // Get all matched records
    db.Where("name <> ?", "jinzhu").Find(&users)
    // SELECT * FROM users WHERE name <> 'jinzhu';
    
    // IN
    db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
    // SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
    
    // LIKE
    db.Where("name LIKE ?", "%jin%").Find(&users)
    // SELECT * FROM users WHERE name LIKE '%jin%';
    
    // AND
    db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
    // SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
    
    // Time
    db.Where("updated_at > ?", lastWeek).Find(&users)
    // SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
    
    // BETWEEN
    db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
    // SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
    
    //-----------------------------筛选特定字段-------------------------
    db.Select("name", "age").Find(&users)
    // SELECT name, age FROM users;
    
    db.Select([]string{"name", "age"}).Find(&users)
    // SELECT name, age FROM users;
    
    db.Table("users").Select("COALESCE(age,?)", 42).Rows()
    // SELECT COALESCE(age,'42') FROM users;
    
    //-----------------------------Struct & Map 条件-------------------------
    // Struct
    db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
    // SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
    
    // Map
    db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
    // SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
    
    // Slice of primary keys
    db.Where([]int64{20, 21, 22}).Find(&users)
    // SELECT * FROM users WHERE id IN (20, 21, 22);
    
    //-----------------------------内联条件-------------------------
    // Get by primary key if it were a non-integer type
    db.First(&user, "id = ?", "string_primary_key")
    // SELECT * FROM users WHERE id = 'string_primary_key';
    
    // Plain SQL
    db.Find(&user, "name = ?", "jinzhu")
    // SELECT * FROM users WHERE name = "jinzhu";
    
    db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
    // SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
    
    // Struct
    db.Find(&users, User{Age: 20})
    // SELECT * FROM users WHERE age = 20;
    
    // Map
    db.Find(&users, map[string]interface{}{"age": 20})
    // SELECT * FROM users WHERE age = 20;
    
    //-----------------------------Not条件-------------------------
    db.Not("name = ?", "jinzhu").First(&user)
    // SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;
    
    // Not In
    db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
    // SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
    
    // Struct
    db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
    // SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;
    
    // Not In slice of primary keys
    db.Not([]int64{1,2,3}).First(&user)
    // SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
    
    //-----------------------------Or条件-------------------------
    db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
    // SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
    
    // Struct
    db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
    // SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
    
    // Map
    db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
    // SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
    
    //-----------------------------Order排序-------------------------
    db.Order("age desc, name").Find(&users)
    // SELECT * FROM users ORDER BY age desc, name;
    
    // Multiple orders
    db.Order("age desc").Order("name").Find(&users)
    // SELECT * FROM users ORDER BY age desc, name;
    
    db.Clauses(clause.OrderBy{
      Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
    }).Find(&User{})
    // SELECT * FROM users ORDER BY FIELD(id,1,2,3)
    
    //-----------------------------Limit和Offset-------------------------
    db.Limit(3).Find(&users)
    // SELECT * FROM users LIMIT 3;
    
    // Cancel limit condition with -1
    db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
    // SELECT * FROM users LIMIT 10; (users1)
    // SELECT * FROM users; (users2)
    
    db.Offset(3).Find(&users)
    // SELECT * FROM users OFFSET 3;
    
    db.Limit(10).Offset(5).Find(&users)
    // SELECT * FROM users OFFSET 5 LIMIT 10; // 分页
    
    // Cancel offset condition with -1
    db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
    // SELECT * FROM users OFFSET 10; (users1)
    // SELECT * FROM users; (users2)
    
    //-----------------------------Group by和Having-------------------------
    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` LIMIT 1
    
    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"
    
    //-----------------------------Distinct-------------------------
    db.Distinct("name", "age").Order("name, age desc").Find(&results)
    
    //-----------------------------Scan-------------------------
    //Scanning results into a struct works similarly to the way we use Find
    type Result struct {
      Name string
      Age  int
    }
    var result Result
    db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
    
    // Raw SQL
    db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    3、改[Update]

    更新指定记录

    //-----------------------------保存所有字段-------------------------
    db.First(&user)
    user.Name = "jinzhu 2"
    user.Age = 100
    db.Save(&user)
    // UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', 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 active=true;
    
    // User 的 ID 是 `111`
    db.Model(&user).Update("name", "hello")
    // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
    
    // 根据条件和 model 的值进行更新
    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;
    
    //-----------------------------更新多列-------------------------
    // 根据 `struct` 更新属性,只会更新非零值的字段
    db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
    // UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
    
    // 根据 `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;
    
    //-----------------------------更新选定字段-------------------------
    // 使用 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})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    更新批量记录

    //-----------------------------批量更新-------------------------
    // 根据 struct 更新
    db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
    // UPDATE users SET name='hello', age=18 WHERE role = 'admin';
    
    // 根据 map 更新
    db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
    // UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
    
    //-----------------------------禁止全局更新-------------------------
    //如果在没有任何条件的情况下执行批量更新,默认情况下,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误。对此,必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式
    db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClause
    
    db.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")
    // UPDATE users SET `name` = "jinzhu" WHERE 1=1
    
    db.Exec("UPDATE users SET name = ?", "jinzhu")
    // UPDATE users SET name = "jinzhu"
    
    db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
    // UPDATE users SET `name` = "jinzhu"
    
    //-----------------------------更新记录数-------------------------
    // 通过 `RowsAffected` 得到更新的记录数
    result := db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
    // UPDATE users SET name='hello', age=18 WHERE role = 'admin';
    
    result.RowsAffected // 更新的记录数
    result.Error        // 更新的错误
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    更新hook【更新钩子】

    //-----------------------------更新 Hook-------------------------
    //GORM 支持 BeforeSave、BeforeUpdate、AfterSave、AfterUpdate 钩子,这些方法将在更新记录时被调用
    func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
        if u.Role == "admin" {
            return errors.New("admin user not allowed to update")
        }
        return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4、删[Delete]

    删除指定记录

    //-----------------------------删除一条记录-------------------------
    //删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete
    // 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";
    
    //-----------------------------根据主键删除-------------------------
    db.Delete(&User{}, 10)
    // DELETE FROM users WHERE id = 10;
    
    db.Delete(&User{}, "10")
    // DELETE FROM users WHERE id = 10;
    
    db.Delete(&users, []int{1,2,3})
    // DELETE FROM users WHERE id IN (1,2,3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    删除批量记录

    //-----------------------------批量删除-------------------------
    db.Where("email LIKE ?", "%jinzhu%").Delete(&Email{})
    // DELETE from emails where email LIKE "%jinzhu%";
    
    db.Delete(&Email{}, "email LIKE ?", "%jinzhu%")
    // DELETE from emails where email LIKE "%jinzhu%";
    
    //-----------------------------禁止全局删除-------------------------
    //如果在没有任何条件的情况下执行批量删除,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误。对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式
    db.Delete(&User{}).Error // gorm.ErrMissingWhereClause
    
    db.Where("1 = 1").Delete(&User{})
    // DELETE FROM `users` WHERE 1=1
    
    db.Exec("DELETE FROM users")
    // DELETE FROM users
    
    db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
    // DELETE FROM users
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    软删除

    如果model包含gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!拥有软删除能力的模型调用 Delete 时,记录不会从数据库中被真正删除。但 GORM 会将 DeletedAt 置为当前时间,并且不能再通过普通的查询方法找到该记录。

    //-----------------------------软删除-------------------------
    //By default, gorm.Model uses *time.Time as the value for the DeletedAt field, and it provides other data formats support with plugin gorm.io/plugin/soft_delete
    import "gorm.io/plugin/soft_delete"
    type User struct {
      ID        uint
      Name      string                `gorm:"uniqueIndex:udx_name"`
      DeletedAt soft_delete.DeletedAt `gorm:"uniqueIndex:udx_name"`
      // DeletedAt soft_delete.DeletedAt `gorm:"softDelete:nano"`
      //IsDel soft_delete.DeletedAt `gorm:"softDelete:flag"`
    }
    
    // Query
    SELECT * FROM users WHERE is_del = 0;
    
    // Delete
    UPDATE users SET is_del = 1 WHERE ID = 1;
    
    // user 的 ID 是 `111`
    db.Delete(&user)
    // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
    
    // 批量删除
    db.Where("age = ?", 20).Delete(&User{})
    // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
    
    // 在查询时会忽略被软删除的记录
    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    删除hook【删除钩子】

    //-----------------------------删除 Hook-------------------------
    func (u *User) BeforeDelete(tx *gorm.DB) (err error) {
        if u.Role == "admin" {
            return errors.New("admin user not allowed to delete")
        }
        return
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5、原生sql

    原生查询 SQL

    //-----------------------------原生查询 SQL 和 Scan-------------------------
    type Result struct {
      ID   int
      Name string
      Age  int
    }
    
    var result Result
    db.Raw("SELECT id, name, age FROM users WHERE name = ?", 3).Scan(&result)
    
    db.Raw("SELECT id, name, age FROM users WHERE name = ?", 3).Scan(&result)
    
    var age int
    db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)
    
    var users []User
    db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    原生 Exec SQL

    //-----------------------------Exec 原生 SQL-------------------------
    db.Exec("DROP TABLE users")
    db.Exec("UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})
    
    // Exec with SQL Expression
    db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Row和Rows

    //获取 *sql.Row 结果
    // 使用 GORM API 构建 SQL
    row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
    row.Scan(&name, &age)
    
    // 使用原生 SQL
    row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
    row.Scan(&name, &age, &email)
    
    // 原生 SQL
    rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
    defer rows.Close()
    for rows.Next() {
      rows.Scan(&name, &age, &email)
      // 业务逻辑...
    }
    
    rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
    defer rows.Close()
    
    var user User
    for rows.Next() {
      // ScanRows 将一行扫描至 user
      db.ScanRows(rows, &user)
      // 业务逻辑...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    minio文件迁移
    解决ios 调用微信JSSDK 签名失败
    vue导航栏下拉菜单(附带展开收缩动画)
    IO流高级流
    为什么 GraphQL 可以取代 Redux?
    TCP | 你真的懂 HTTP 吗?
    【Vue3 源码解析】v-model 和 emit
    JAVA-分页查询
    音视频技术开发周刊 | 258
    Java直接量(字面量)及 Java运算符优先级
  • 原文地址:https://blog.csdn.net/qq_41822345/article/details/126072106