使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作 | 青训营

99 阅读7分钟

数据库保存的表,字段与程序中的实体类之间是没有关联的,因此操作数据库完成数据持久化需要特殊的解决方案。这个方案主要可以分为两种,一是硬编码的方式,例如在Golang中使用原生库"github.com/go-sql-driver/mysql" 来访问MySQL数据库;二是使用ORM库,例如GORM。

硬编码方案存在以下缺点:
1.持久化层缺乏弹性。一旦出现业务需求的变更,就必须修改持久化层的接口;
2.持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,修改持久化的相关程序代码,增加了软件的维护难度

因此就有了ORM技术。ORM全称是:Object Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。举例来说就是,我定义一个对象,那就对应着一张表,这个对象的实例,就对应着表中的一条记录。

GORM 是一个在 Go 语言中使用的对象关系映射(ORM)库,它可以方便地进行数据库的增删改查操作,同时提供了一系列的功能来简化数据库操作过程。本文将介绍如何使用 GORM 连接 MySQL 数据库,并进行基本的增删改查操作。

1、通过DSN连接数据库

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

func main() {      
    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(数据来源名称),才能让网页可以知道数据库所在的位置以及数据库相关的属性。使用DSN的好处还有,如果移动数据库档案的位置,或是换成别种类型的数据库,只要重新设定 DSN 就好了,不需要去修改原来使用的程序。

参数介绍:

  • user:用户名
  • pass: 密码
  • 127.0.0.1: MySQL默认本地地址,也就是localhost
  • 3306: MySQL默认端口
  • dbname: 数据库名称
  • charset: 字符集编码
  • parseTime: 把数据库datetime和date类型转换为golang的time.Time类型
  • loc: 使用系统本地时区
  • gorm.config{} 中可以进行一些高级配置,详情请看官方文档。

2、GO CRUD! —— 实现增删改查操作

2.1、增

// 创建记录并为指定的字段分配值
db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("username", 18, "2023-08-12 11:05:21.775") 

// 创建记录并忽略指定的字段分配值
db.Omit("Name", "Age", "CreatedAt").Create(&user) 
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")

Create()方法接收一个结构体实例指针,在成功插入后,gorm会将整个记录回写到该实例中。

2.2、删

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

2.3、改

2.3.1、方法1

db.First(&user)  
user.Name = "ggg"
db.Save(&user)

2.3.2、方法2

db.Save(&User{id: 10, Name: "ggg", Age: 100})

2.3.3、注意

如果传入Save()的结构体中不含有id字段,则会插入新的记录。

2.4、查

// 获取第一条记录 
res := db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
res := db.Where("name = ?",  "username").First(&user)

// Take, Last, Find同样可以使用Where()来条件检索

// 获取一条记录,没有指定排序字段
res := db.Take(&user)
// SELECT * FROM users LIMIT 1;

// 获取最后一条记录(主键降序)
res := db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

res := db.Find(&user)
// SELECT * FROM user ORDER BY id;

res.RowsAffected   // 返回找到的记录数
res.Error          // returns error or nil

值得关注的细节:

  • 使用“查”的一种场景是判断表中是否有符合该条件的记录。First(),Take()与Last()方法在没找到匹配的记录时会返回ErrRecordNotFound错误,而使用Find()方法则不会返回该记录,即使使用了db.Limit(1).Find(&user)来修饰。另外需要注意的是在&user原来的值为nil,在使用Find(&user)方法后,即使找不到匹配的记录,&user的值也将不是nil,因此不能通过检查&user的值是否为nil来判断是否找到了匹配的记录,我认为使用单一struct来接住Find()的查询结果,这个实现的行为是不确定的;

  • 若查找的数据很明确只有一条(例如按用户名查找,且用户名是不重复的),则使用Find()是低效的,因为在查找到匹配的第一条数据后仍然会继续查找剩余行,这是性能不高,而First(),Take()与Last()则都是找到一条后就返回;

-- 对于明确只有一条匹配记录或者只需要其中一条的场景,使用First(),Take()与Last(),并使用ErrRecordNotFound来判断是否找到了记录;

-- 对于可能匹配到多条记录,且都需要拿到的场景,使用Find(),并使用slice接住结果,通过查看slice的长度来判断是否找到了记录。

3、时间

datetime 对应 time.Time,注意在连接时需要在dsn中加上&parseTime=True。

4、总结

GORM(Go Object Relational Mapping) 是 Go 编程语言中的一个 ORM(对象关系映射)库,它用于简化在 Go 应用程序中与关系型数据库进行交互的过程。ORM 是一种编程技术,用于将数据库表和数据映射到程序中的对象,以便以面向对象的方式操作数据库。

  • GORM 的作用:
  1. 简化数据库操作:GORM 提供了一种更直观和简单的方式来执行数据库操作,使开发人员可以使用面向对象的语法,而不需要编写大量的 SQL 查询语句。
  2. 提高开发效率: 使用 GORM 可以减少手动编写 SQL 查询和处理数据库连接的工作量,从而提高开发效率。
  3. 避免 SQL 注入:GORM 会自动将参数化查询应用到 SQL 查询中,从而减少 SQL 注入的风险。
  4. 数据库无关性:GORM 支持多种数据库,包括 MySQL、PostgreSQL、SQLite 等,使得应用程序在不同数据库之间切换更加容易。
  5. 模型定义:GORM 允许你通过结构体定义数据模型,从而将数据库表映射到 Go 的结构体上。
  • GORM 的优点:
  1. 简化查询操作:GORM 提供了丰富的查询方法,包括条件查询、排序、分页等,使得查询操作变得简单易用。
  2. 自动映射:GORM 可以自动将数据库表的记录映射到 Go 结构体,大大减少了手动的映射工作。
  3. 事务支持:GORM 提供了事务管理功能,使你能够以一种安全的方式执行数据库操作。
  4. 钩子(Hooks)支持:GORM 支持在模型对象的生命周期事件中执行钩子函数,例如在创建、更新、删除等操作前后执行特定的逻辑。
  • GORM 的缺点:
  1. 性能:相对于手动编写 SQL 查询,ORM 框架可能会引入一定的性能开销。尤其是在大规模数据操作时,一些复杂查询可能会影响性能。
  2. 学习曲线:尽管 GORM 试图简化数据库操作,但仍然需要学习其 API 和用法。对于新手来说,掌握 ORM 可能需要一些时间。
  3. 灵活性:有时候复杂的查询需求可能无法直接使用 GORM 提供的方法,需要编写原始 SQL 查询来满足特定需求。

综合考虑,GORM 在大多数情况下可以显著简化数据库操作,提高开发效率,实在不行还可以使用原生sql语句来进行操作。同时,根据项目的特定需求,开发人员需要权衡使用 GORM 的优势和劣势,以确保选择了适合的工具。