Gorm初阶使用指南 | 青训营

243 阅读6分钟

概述

中文官网:GROM

GORM是一个使用Go语言编写的ORM框架,它提供了强大的功能和简洁的API,让数据库操作变得更加简单和易维护。GORM支持多种数据库,包括MySQL、PostgreSQL、SQLite、Oracle等。它还提供了许多有用的功能,如预加载、事务、复合主键、SQL生成器等。

准备

选择Mysql作为测试数据库

  • 部署指南

    dockercompose本地一键部署docker-compose 本地一键部署
    # docker-compose.yaml
    version: "3"
    services:
      db:
        image: mysql:latest
        container_name: mysql
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: root
        volumes:
          - ./data:/var/lib/mysql
        ports:
          - "3306:3306"
    
  • 启动容器

​ docker-compose up -d

选择Navicat Premium 16作为Mysql可视化客户端

  • 文末贴有免费获取方式

引入依赖

go get -u gorm.io/gormgo

"gorm.io/driver/mysql"

连接到Mysql

定义连接信息 | dsn

dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"

字段说明:user | 数据库用户名 ,pass | 密码

​ tcp(127.0.0.1:3306) | tcp连接(本地MySQL部署IP:mysql服务端口/默认3306)

​ dbname | 数据库名 , charset=utf8mb4 | utf8mb4编码

​ parseTime=True&loc=Local | 时间参数,格式化并采用本地时间

Tips:

​ 对于本地Linux,最好查看一下本地时间是否跟国内一致,不然入库会出现时区不对造成少几个小时的情况

date | 查看本地时间

timedatectl set-timezone Asia/Shanghai | 设置国内时区

mysql驱动配置

字段
DSNdsn
DefaultStringSize256 // string 类型字段的默认长度
DisableDatetimePrecisiontrue // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndextrue, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumntrue // 用 change 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列

具体代码

根据连接信息和驱动配置得到 db 对象,然后就可以进行mysql数据库操作

	dsn := "root:root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.New(mysql.Config{
		DSN:                       dsn,   // DSN data source name
		mysql其他驱动配置...
	}), &gorm.Config{})

基本用法

前提

定义模型 | 映射到数据库表字段

后续都以用户表为例

type User struct {
	gorm.Model // gorm 自带字段 ID | CreatedAt | UpdatedAt | DeletedAt 
	Name       string
	Age        int
}

创建

Create Api

  • 创建一条记录

    语句:user:=&User{Name: "汐瀼",Age:100}

    db.Create()

image.png

  • 创建多条记录

    语句:users := []*User{{Name: "xiao li", Age: 10},{Name: "xiao ming", Age: 333}, } db.Create(users)

  • 创建记录并为指定的字段分配值

    语句:db.Select("Name", "Age").Create(&user)

  • 创建记录并忽略要省略的传递字段的值

    语句:db.Omit("Name", "Age").Create(&user)

  • 批量插入 | 高效分批次插入

    gorm配置,可以设置批次

    &gorm.Config{CreateBatchSize: 1000,}

    语句:db.CreateInBatches(users, 100)

  • 根据 Map 创建 | 支持切片批量插入

    语句:db.Model(&User{}).Create(map[string]interface{}{ "Name": "ttt", "Age": 18,})

  • 默认值 | 针对模型定义

    Name string gorm:"default:游客" Age int64 gorm:"default:18"

  • 钩子 Hook | 创建周期触发的事件

    需要实现 BeforeSave BeforeCreate AfterSave AfterCreate 方法

    实现创建之前的Hook

    func (u *User) BeforeCreate(tx *gorm.DB) (err error)

​ 跳过Hook

DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)

查询

First

  • 获取第一条记录(主键升序)

    语法:db.First(&user)

  • 主键检索

    语法:db.First(&user, 10)

    db.First(&user, "10")

    db.Find(&users, []int{1,2,3}) // in

Take

  • 获取一条记录,没有指定排序字段

    语法:db.Take(&user)

    db.Where(&User{Name: "jinzhu", Age: 20}).First(&user) // struct

    db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users) // map

    db.Where([]int64{20, 21, 22}).Find(&users) // 主键id

Last

  • 获取最后一条记录(主键降序)

    语法:db.Last(&user)

Find

  • 检索全部字段

    语法:result := db.Find(&users)

  • 条件查询

    语法:db.Where("name = ?", "lily").First(&user) // 一条

    db.Where("name <> ?", "lily").Find(&users) // 所有

    db.Where("name IN ?", []string{"lily", "jack"}).Find(&users) // in

    db.Where("name LIKE ?", "%lily%").Find(&users) // like

    db.Where("name = ? AND age >= ?", "lily", "22").Find(&users) // and

    db.Limit(10).Offset(5).Find(&users) // limit offset 做分页

Scan

  • 语句:db.Model(&User{}).Select("name", "age").Where("name = ?", "Antonio").Scan(&result)

更新

Save

  • 保存所有字段

    语句:db.Save(&User{Name: "jinzhu", Age: 100})

Update

  • 更新单个列

    语句:db.Model(&user).Update("name", "hello")

  • 更新多列

​ 语句:db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})

  • 批量更新

    语句:db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})

更新 Hook

  • BeforeSave, BeforeUpdate, AfterSave, AfterUpdate

    func (u *User) BeforeUpdate(tx *gorm.DB) (err error)

删除

Delete

  • 删除一条记录

    语句:db.Where("name = ?", "jack").Delete(&user)

  • 根据主键删除

    语句:db.Delete(&User{}, 10)

    db.Delete(&User{}, "10")

    db.Delete(&users, []int{1,2,3}) // in

  • 批量删除

    语句:db.Where("name LIKE ?", "%lily%").Delete(&User{})

  • 软删除 | 模型包含 gorm.DeletedAt 字段

    说明:当调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时 间,而后的一般查询方法将无法查找到此条记录。

    查找被软删除的记录:Unscoped()

    db.Unscoped().Where("age = 20").Find(&users)

  • 永久删除

    语句:db.Unscoped().Delete(&order)

Hook

  • BeforeDelete、AfterDelete

    func (u *User) BeforeDelete(tx *gorm.DB) (err error)

高级用法

复用多种数据库

我们知道,gorm是orm框架,支持多种数据库,包括MySQL、PostgreSQL、SQLite、Oracle等,那么我们可以抽象一个方法,根据不同数据库实例化对应gorm.DB

实现

初始化gorm配置

  • 定义 gorm 结构体

    type _gorm struct{}

  • 实现 func (g *_gorm) Config(prefix string, singular bool) *gorm.Config 方法

    暴露 gorm 配置

    func (g *_gorm) Config(prefix string, singular bool) *gorm.Config {
    	config := &gorm.Config{
    		// 命名策略
    		NamingStrategy: schema.NamingStrategy{
    			TablePrefix:   prefix,   // 表前缀,在表名前添加前缀,如添加用户模块的表前缀 user_
    			SingularTable: singular, // 是否使用单数形式的表名,如果设置为 true,那么 User 模型会对应 users 表
    		},
    		// 是否在迁移时禁用外键约束,默认为 false,表示会根据模型之间的关联自动生成外键约束语句
    		DisableForeignKeyConstraintWhenMigrating: true,
    		// 其他配置...
    	}
    	// TODO 日志配置器,替换gorm自定义logger
    	...
    	return config
    }
    // 暴露 gorm 配置
    var Gorm = (*_gorm)(nil)
    
  • 以 mysql数据库为例,初始化mysql连接,最终得到 gorm.DB 对象 | 其他数据库一样

    // GormMysql 初始化Mysql数据库
    func GormMysql() *gorm.DB {
    	mysqlConfig := mysql.Config{
    		DSN:                       dsn, // DSN data source name
    		// 其他配置...
    	}
    	// 打开数据库连接 驱动 gorm配置
    	db, err := gorm.Open(mysql.New(mysqlConfig), Gorm.Config(m.Prefix, m.Singular))
    	// 其他配置...
        // TODO 实现数据迁移
    	return db
    }
    
  • 接下来,实现数据库初始化方法,根据传入的参数,判断选择哪个数据库

    func InitialDB(db string) *gorm.DB {
    	switch db {
    	case "mysql":
    		return GormMysql()
    	case "postsql":
    		return GormPgsql()
    	default:
    		return GormMysql()
    	}
    }
    

自定义数据类型

实现接口

自定义的数据类型必须实现 ScannerValuer 接口,以便让 GORM 知道如何将该类型接收、保存到数据库

Scanner 接口

type JSON json.RawMessage
// 实现 sql.Scanner 接口,Scan 将 value 扫描至 Jsonb
func (j *JSON) Scan(value interface{}) error

Valuer 接口

// 实现 driver.Valuer 接口,Value 返回 json value
func (j JSON) Value() (driver.Value, error) {
  if len(j) == 0 {
    return nil, nil
  }
  return json.RawMessage(j).MarshalJSON()
}

分页

场景:从http请求中获取分页参数 offset 和 limit 对分页参数进行处理 返回 func(db *gorm.DB) *gorm.DB 函数对象,使用Scopes作用域复用db对象,实现分页

func Paginate(r *http.Request) func(db *gorm.DB) *gorm.DB {
  return func (db *gorm.DB) *gorm.DB {
    q := r.URL.Query()
    page, _ := strconv.Atoi(q.Get("page"))
    if page <= 0 {
      page = 1
    }

    pageSize, _ := strconv.Atoi(q.Get("page_size"))
    switch {
    case pageSize > 100:
      pageSize = 100
    case pageSize <= 0:
      pageSize = 10
    }

    offset := (page - 1) * pageSize
    return db.Offset(offset).Limit(pageSize)
  }
}

db.Scopes(Paginate(r)).Find(&users)

wx搜索 汐瀼爱分享,回复:数据库 获取数据库客户端软件