GO语言GORM框架介绍|青训营笔记

74 阅读5分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 6 天

1. 概述

我将主要介绍如下知识点:

  1. Gorm 的基本使用
  2. Gorm 的增删改查

2. Gorm 的基本使用

2.1 导入 Gorm 和 MySQL 数据驱动

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

2.2 定义结构体

  • 也就是 gorm model,对应数据库里的一张表

  • 可以使用 default 标签为字段定义默认值

    如:Age int64 `gorm:"default:18"`

type Product struct { 
   Code  string
   Price uint
}

2.3 为 model 定义表名

  • gorm 提供了 TableName() 这个接口
func (p Product) TableName() string { 
   return "product" // 返回的字符串即表名
}

2.4 快速入门

关于什么是 dsn,感兴趣可以看:github.com/go-sql-driv…

func main() {
   // 连接数据库(需要传递一个 dsn)
   db, err := gorm.Open(
      mysql.Open("user:pass@(127.0.0.1:3306)/dname?charset=utf8mb4&parseTime=True&loc=Local"),
      &gorm.Config{})
   if err != nil {
      panic("failed to connect database")
   }

   // 创建数据 Create() 方法支持创建一条(传递对象)和多条数据(传递数组)
   db.Create(&Product{Code: "D42", Price: 100})

   // 查询数据 先声明一个结构体, 然后使用 First() 方法(注意是传递指针)
   var product Product
   db.First(&product, 1)                 // 根据整型主键查找
   db.First(&product, "code = ?", "D42") // 查找 code 字段值为 D42 的记录

   // 更新数据 - 用 Update() 方法将 product 的 price 更新为 200
   db.Model(&product).Update("Price", 200)
   // 更新数据 - 用 Updates() 方法更新多个字段
   db.Model(&product).Updates(Product{Price: 200, Code: "F42"})                    // 仅更新非零值字段
   db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) // 传递 map 可以更新零值

   // 删除数据
   db.Delete(&product, 1)
}

2.5 Gorm 的默认约定

  • Gorm 使用名为 ID 的字段作为主键

  • 使用结构体名的 蛇形命名 作为表名

    如:对于结构体 User,根据约定,其表名为 users

  • 字段名的蛇形作为列名

  • 使用 CreatedAt、UpdatedAt 字段作为创建、更新时间

3. GORM 支持的数据库

GORM 目前支持: MySQL,SQL Server,PostgreSQL,SQLite

  • GORM 通过驱动来连接数据库,如果需要连接其它类型的数据库,可以复用/自行开发驱动

4. GORM 的 crud

4.1 创建数据

  • 如下是定义的结构体

    type Product struct {
       ID    uint   `gorm:"primarykey"`
       Code  string `gorm:"column: code"`
       Price uint   `gorm:"column: user_id"`
    }
    
  • 创建一条数据

    注意:gorm 是链式调用,使用 Creat() 后会返回一个gorm 对象,可以通过返回的对象获取 error

    p := &Product{Code: "D42"}
    res := db.Create(p)    // 因为 gorm 是链式调用, 使用 Creat() 后会返回一个gorm对象
    fmt.Println(res.Error) // 获取 err
    fmt.Println(p.ID)      // 返回插入数据的主键
    
  • 创建多条数据

    需要去传递一个 list 结构体

    products := []*Product{{Code: "D41"}, {Code: "D42"}, {Code: "D43"}}
    res = db.Create(products) // 批量创建数据
    fmt.Println(res.Error)    // 获取 err
    for _, p := range products {
       fmt.Println(p.ID)
    }
    

4.2 查询数据

  • 如下是定义的结构体

    type User struct {
       ID   int64
       Name string `gorm:"default:galeone"`
       Age  int64  `gorm:"default:18"`
    }
    
  • 查询第一条数据

    使用 First 时,查询不到数据则返回 ErrRecordNotFound

    // 获取第一条记录(主键升序)
    u := &User{}
    db.First(u) // SELECT * FROM users ORDER BY id LIMIT 1;
    
  • 条件查询

    使用 Find 查询多条数据,查询不到数据不会返回错误

    // 查询多条数据
    users := make([]*User, 0)
    result := db.Where("age > 10").Find(&users) // SELECT * FROM users where age > 10
    fmt.Println(result.RowsAffected)            // 返回找到的记录数, 相当于 `len(users)`
    fmt.Println(result.Error)                   // return error
    
    // IN => SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
    db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
    
    // LIKE => SELECT * FROM users WHERE name LIKE '%jin%';
    db.Where("name LIKE ?", "%jin%").Find(&users)
    
    // AND => SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
    db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
    
  • 使用结构体或者map作为查询条件(较少用)

    当使用结构体作为条件查询时,GORM 只会查询非零值字段;若字段为 0、”“、false 或其它零值,使用 Map 来构建查询条件

    // SELECT * FROM users WHERE name = "jinzhu";
    db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
    // SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
    db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
    

4.3 更新数据

  • 更新单个列

    // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE age > 18
    db.Model(&User{ID: 111}).Where("age > ?", 18).Update("name", "hello")
    
  • 更新多个列

    根据 `struct` 更新属性,只会更新非零值的字段

    // UPDATE users SET name='hello', age=18, updated_at='2013-11-17 21:34:10' WHERE id = 111;
    db.Model(&User{ID: 111}).Updates(User{Name: "hello", Age: 18})
    
  • 根据 `map` 更新属性

    // UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id = 111;
    db.Model(&User{ID: 111}).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
    
  • 更新选定字段

    下面这段代码中虽然 map 中有多个键值对,但是其实只会更新 Select() 选定的 name 字段

    // UPDATE users SET name='hello' WHERE id=111;
    db.Model(&User{ID: 111}).Select("name").Updates(map[string]interface{}{"name": "hello",
       "age": 18, "actived": false})
    

4.4 删除数据

物理删除

// DELETE FROM users WHERE id = 10;
db.Delete(&User{}, 10)

// DELETE FROM users WHERE id IN (1,2,3);
db.Delete(&User{}, []int{1, 2, 3})

// DELETE FROM users WHERE name LIKE "%jinzhu%";
db.Where("name LIKE ?", "%jinzhu%").Delete(User{})

// DELETE FROM users WHERE name LIKE "%jinzhu%";
db.Delete(User{}, "name LIKE ?", "%jinzhu%")

软删除

GORM 提供了 gorm.DeletedAt 用于帮助用户实现软删

拥有软删能力的 Model 调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间,并且你不能再通过正常的查询方法找到该记录

使用 Unscoped 可以查询到被软删的数据

  • 拥有软删能力的 model 定义如下:

    type User struct {
       ID      int64
       Name    string `gorm:"default:galeone"`
       Age     int64  `gorm:"default:18"`
       Deleted gorm.DeletedAt
    }
    
  • 删除一条

    // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
    u := User{ID: 111}
    db.Delete(&u)
    
  • 批量删除

    // UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;
    db.Where("age = ?", 20).Delete(&User{})
    users := make([]*User, 0)
    
    // 在查询时会忽略被软删的记录
    // SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;
    db.Where("age = 20").Find(&users)
    
    // 在查询时不会忽略被软删的记录
    // SELECT * FROM users WHERE age = 20;
    db.Unscoped().Where("age = 20").Find(&users)
    

5. 总结

GORM 是 Go 语言中一款性能极好的 ORM 库,对开发人员相对是比较友好的。

参考: