GORM 连接数据库&增删查改 | 青训营

164 阅读6分钟

概述

  • 什么是ORM:Object Relational Mapping 对象关系映射
  • Gorm是一个已经迭代了10+年的功能强大的ORM框架。
  • Gorm是go的一个数据库连接及交互框架,主要是把struct类型和数据库记录进行映射,数据库语句复杂的情况下可以直接手写语句,一般用于连接关系型数据库。
  • 对应关系:数据表<-->结构体;数据行<-->结构体实例;字段<-->结构体字段
  • 优点:提高开发效率
  • 缺点:牺牲执行性能;牺牲灵活性,弱化SQL能力
  • GORM 官方支持的数据库类型有:MySQL、PostgreSQL、SQLite、SQL Server 和 TiDB。GROM通过驱动来连接数据库,如果需要连接其它类型的数据库,可以复用或自行开发驱动。文中使用MySQL数据库做示例。

1. 安装

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql //这里安装的是MySQL驱动,换用其它数据库修改最后一个字段即可

2. 连接数据库

链接不同的数据库 都需要导入相对应数据库的驱动程序,GROM已经包装了一些驱动程序,直接按需要导入驱动即可。

连接MySQL

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

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

什么是dsn:Data Source Name,数据源名称。(详细可以打开上方代码中注释的链接查看)

注意:想要正确的处理 time.Time,需要带上parseTime 参数。要支持完整的 UTF-8 编码,需要将 charset=utf8更改为charset=utf8mb4。

MySQL 驱动程序提供了一些高级配置可以在初始化过程中使用,例如:

db, err := gorm.Open(mysql.New(mysql.Config{
  DSN: "gorm:gorm@tcp(127.0.0.1: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{})

3. 创建数据表

创建表之前,我们需要定义一个gorm.Model结构体使之与数据表对应。

这里用UserInfo做示例

type UserInfo struct {
	ID     uint
	Name   string
	Gender string
	Hobby  string
}

创建表-自动迁移:使用db.AutoMigrate(&结构体名{})(自动迁移就是让新增的结构体字段自动增加到数据表中)

4. 增删查改

4.1. 创建(增加)

4.1.1. 创建记录(数据行)

使用db.Create()

创建一条数据:

运行后可以发现db1数据库中出现了表user_infos,表里也有一条我们创建的数据

同样可以用Create创建多条数据:

4.1.2. Upsert和冲突

使用clause.OnConflict处理数据冲突

4.1.3. 默认值

通过使用defalut标签为字段定义默认值

4.2. 删除

4.2.1. 物理删除

删除一条记录时,删除对象需要指定主键,否则会触发批量删除,删除所有匹配的记录。

db.Delete(&UserInfo{}, 1)
//删除一条id为1的记录 DELETE FROM user_infos WHERE id = 1;

db.Where("Name LIKE ?", "%小红%").Delete(&UserInfo{})
//批量删除名字为“小红”的记录 DELETE from emails where email LIKE "%小红%";

删除操作前:

删除一条id为1的记录后:

批量删除名字为“小红”的记录后:

4.2.2. 软删除

GORM提供了gorm.DeletedAt用于帮助用户实现软删除。拥有软删除能力的Model调用Delete时,记录不会被从数据库中真正删除。但GROM会将DeleteAt置为当前时间,并且不能通过正常的查询方法找到该记录。

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

4.3. 查询

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

运行结果:

选择特定字段查询:Select允许指定要从数据库中检索的字段,否则将默认选择所有字段。

tips:

  1. 如果想避免ErrRecordNotFound错误,可以使用Find,比如db.Limit(1).Find(&user)Find方法可以接受struct和slice的数据。对单个对象使用Find而不带limit,db.Find(&user)将会查询整个表并且只返回第一个对象,这是性能不高并且不确定的。
  2. 使用结构体作为条件查询时,GORM只会查询非零值字段。如果字段值为0、''、false或其他零值,该字段将不会被用于构建查询条件。要在查询条件中包含零值可以使用Map来构建查询条件。

4.4. 修改(更新)

  • Save会保存所有的字段,即使字段是零值。Save是一个组合函数。如果保存值不包含主键,它将执行Create,否则它将执行包含所有字段的Update。但是Save会更新 struct中全部的字段,未赋值的字段使用零值来更新,这样极易覆盖数据库中原有的值。
  • 使用Struct更新:使用示例:db.Model(&user).Updates(UserInfo{...}),只会更新非零值,如果需要更新零值可以使用Map更新或使用Select选择字段。
  • 使用Map更新:使用示例:db.Model(&user).Updates(map[string]interface{}{...})
  • 如果想更新指定字段,可以使用SelectOmit

使用Map更新选定字段 举例:

// Select with Map
// 要修改的用户ID为`1`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "zhangsan", "hobby": "篮球"})
// UPDATE user_infos SET name='张三',hobby='篮球' WHERE id=1;

db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "zhangsan", "hobby": "篮球"})
// UPDATE user_infos SET name='张三',hobby='篮球' WHERE id=1;

5. 扩展

GORM 事务

Grom 提供了Begin、Commit、Rollback方法用于事务;同时也提供了Transaction 方法用于自动提交事务,避免用户漏写Commit、Rollback。

Hook

Grom提供了CURD的Hook能力。Hook是在创建、查询、更新、删除等操作之前、之后自动调用的函数。如果任何Hook返回错误,Grom将停止后续的操作并回滚事务

性能提高

对于写操作(创建、更新、删除),为了确保数据的完整性,GROM会将它们封装在事务内部运行,但这会降低性能,可以使用SkipDefaultTransaction关闭默认事务。使用PrepareStmt缓存预编译语句可以提高后续调用的速度。

Grom的生态

Grom拥有丰富的扩展生态,常用扩展有:代码生成工具、分片库方案、手动索引、乐观锁、读写分离等。

如有错误,欢迎指正!

文中只简单叙述了GORM的基础用法,关于更多的GORM用法和细节可以查看官方文档(gorm.io/zh_CN/)