Go三件套之GORM | 青训营笔记

79 阅读3分钟

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

内容源于青训营课堂视频以及一些go文档和自己的经验、理解,若有错误欢迎及时指出

三件套介绍(Gorm/Kitex/Hertz)

  • Gorm:一个迭代了十多年的功能强大的ORM框架
  • Kitex:字节内部的golang微服务RPC框架,高性能、高扩展特点,支持多协议并且有丰富的开源扩展
  • Hertz:字节内部的HTTP框架,最初是在gin基础上再开发的

Gorm的基础使用

模型定义

Models are normal structs with basic Go types, pointers/alias of them or custom types implementing Scanner and Valuer interfaces

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

type User struct {
  ID           uint
  Name         string
  Email        *string
  Age          uint8
  Birthday     *time.Time
  MemberNumber sql.NullString
  ActivedAt    sql.NullTime
  CreatedAt    time.Time
  UpdatedAt    time.Time
}

gorm.Model

自定义配置

// gorm.Model 的定义
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

更多的高级选项可参考模型定义高级选项

连接数据库

目前官方支持的有MySQL, PostgreSQL, SQlite, SQL Server

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{})

CRUD接口

  • 创建

    user := User{Name: "Ltd", Age: 12, Birthday: time.Now()}
    
    result := db.Create(&user) // 通过数据的指针来创建
    
    user.ID             // 返回插入数据的主键
    result.Error        // 返回 error
    result.RowsAffected // 返回插入记录的条数
    
  • 查询

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

    // 获取第一条记录(主键升序)
    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
    
    // 检查 ErrRecordNotFound 错误
    errors.Is(result.Error, gorm.ErrRecordNotFound)
    

    使用结构体作为查询条件时,GORM只会查询非零字段,这意味着若字段为对应类型的零值。那么不会作为查询条件,而需要使用Map来构建查询条件

    db.WHere(&User{Name: "Ltd", Age: 0}).Find(&user)
    // SELECT * FROM users WHERE name = "Ltd"
    
    db.WHere(map[string]interface{}{Name: "Ltd", Age: 0}).Find(&user)
    // SELECT * FROM users WHERE name = "Ltd" AND age = 0
    
  • 更新

    Updates 方法支持 structmap[string]interface{} 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段

    // 根据 `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, "actived": false})
    // UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
    
    

    如需更新零值可以使用Map或Select选择字段

  • 删除

    删除一条记录时,删除对象需要指定主键,否则会触发批量Delete,例如:

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);

db.Where("name LIKE ?", "%Ltd%").Delete(User{})
// DELETE from users where name LIKE "%Ltd%";

如果您的模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

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

使用 Unscoped 找到被软删除的记录

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

永久删除

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