初识GORM
由于是第一次接触后端,所以对于数据库方面的知识也是第一次接触。区别于前端,前端需求中没有对数据记忆性的要求。
GORM是什么
GORM是Go语言的一个ORM对象关系映射库,它提供了高效、简洁和灵活的API来实现Go程序和关系型数据库间数据的交互。在GORM之前,Go程序访问数据库通常需要依赖原始的SQL语句来完成操作,这种方式写SQL语句的效率低下并且容易出错。GORM通过为Go程序员提供一个强大的工具,简化了与关系型数据库的交互,这使得Go程序员不再依赖过多的SQL语句,可以更专注与业务逻辑。
GORM的优点和特性
1. 支持多种数据库
GORM支持主流的关系型数据库,如MySQL、PostgreSQL、SQLite等,这是因为GORM的底层支持了多种数据库驱动。这,开发者可以轻松切换数据库,而不需要修改底层的代码。
2. 自动迁移
GORM提供了自动迁移功能,可以根据定义的结构体自动生成数据库表。这个功能对于快速创建或调整数据表结构非常有帮助。开发者不需要手动创建表和索引,只需要定义相应的Go结构体,就可以利用该特性轻松完成迁移工作。
3. 简的API
GORM提供了一个简单、高效、类型安全的API,来代替写SQL语句进行增删改查,只需要使用方便的链式调用即可完成各种操作。GORM的API支持链式调用,因此可以用一种非常简单的方式完成复杂的查询操作。
4. 预加载
GORM支持Eager Loading(预加载)操作,在查询关联数据时使用预加载机制,可以避免N+1查询问题,也可以大大提高查询效率。在查询关联数据时,预加载机制允许一次性获取所有关联记录,从而大幅减少查询次数,并可以避免使用循环查询数据库的方式来获取嵌套数据。
5. 事务支持
GORM支持事务操作,通过Wrap方法使得实现起来十分简单,并且易于与Golang的defer关键字一起使用。事务是一个非常重要的特性,它可以确保操作的原子性,同时可以保证数据的一致性。
除此之外,GORM还具备其他特性,如Model钩子、复杂的查询支持、代码可测性等,它们可以让开发者更加方便地进行对关系型数据库的访问,并且大大提高了应用的开发效率。
安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
入门示例
从GORM官方文档 “快速入门”中(稍作修改)得到以下代码段示例
package main
import (
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)
首先是导入驱动
- GORM是一个与数据库进行交互的库,并且它并不直接与数据库驱动。相反,GORM是建立在数据库驱动之上的一个ORM层,它封装了数据库驱动的复杂性,为开发者提供了更简洁、高效的API来进行数据库操作。
- GORM与数据库驱动的关系可以被描述为一种嵌套的关系。GORM本身不包含任何数据库驱动的实现,而是依赖于实际的数据库驱动。在使用GORM之前,开发者需要先选择和导入适合自己所使用数据库的驱动包。
数据库驱动是一个实现了对特定数据库的底层操作的库或模块。在Go语言中,每个数据库都有对应的驱动,例如Go程序连接MySQL数据库通常会使用mysql驱动,连接PostgreSQL数据库会使用postgres驱动。
type Product struct {
Code string
Price uint
}
func (p Product) TableName() string{
return "product"
}
随后是结构体定义
-
这段Go语言代码定义了一个结构体
Product,其中包含两个字段Code和Price,分别表示产品代码和价格。接下来定义了一个TableName()方法,该方法的目的是为GORM提供表名。 -
通过在
Product结构体上实现TableName()方法,开发者为该结构体在数据库中对应的表指定一个自定义的表名。在这段代码中,TableName()方法返回的表名是 "product"。 -
这个自定义的表名在GORM的模型声明中使用,GORM会利用这个表名将结构体中的字段映射到数据库表的列。
db, err := gorm.Open(
mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),
&gorm.Config{})
if err != nil {
panic("failed to connect database")
}
连接数据库
- 代码中的
gorm.Open()函数将打开一个MySQL数据库连接,并返回一个 db —— 数据库连接对象。 - 该函数的第一个参数是一个字符串,表示数据库连接的URL(DSN),其中包含了MySQL数据库的连接信息,包括用户名、密码、主机和端口信息、数据库名等。连接字符串中的字符集设置为utf8mb4,解析时间设置为True,本地时区设置为Local。
DSN全称是Data Source Name,可以翻译为数据源名称。在软件开发中,DS常用于指定各种数据源的信息,例如数据库、Web服务、消息队列等。它是一种标准化的方式来指定不同类型的数据源以及如何连接到这些数据源。
以mysql为例:
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
username:password@protocol(address)/dbname?param=value
- 通过这段代码,开发者可以在后续的代码中使用
db对象来执行各种与数据库相关的操作,例如创建表、插入数据、查询数据等。
db.Create(&Product{Code: "D42", Price: 100})
创建数据
db.Create()方法是在打开的db上定义的,它将创建一个新的product记录。该方法的参数是一个Product对象的引用,表示要向数据库中写入哪些信息。- 对于这个方法,如果指定的产品对象的主键或唯一键已经存在于表中,则会返回一个错误。如果执行成功,则会返回受影响的行数
var product Product
db.First(&product, 1)
db.First(&product, "code = ?", "D42")
查询数据
- 分别使用两种方式查找记录。第一种方式是通过整数类型的主键ID查找,第二种方式是通过指定某个字段的值进行查找。
- 在第一种方式中,
&product表示通过引用来承载查询到的记录。指定参数是数字类型的主键ID值1,因此将查找具有ID为1的 Product 记录,并将结果写入 Product 引用product中。 - 在第二种方式中,查询条件中使用 “?”替代值表明使用占位符语句,则在第二个参数中提供具体值,这个具体值是字符串类型的 "D42",表示查询字段 code 值为 "D42" 的记录。所以这一行代码会在数据库中查找与"code = 'D42'"条件匹配的第一个记录,并将其写入到
product引用中。
db.Model(&product).Update("Price", 200)
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // 仅更新非零值字段
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
更新数据
- 在第一行代码中,
db.Model(&product)表示将数据库中的product记录(第一段代码中查询到的),包装到一个 GORM 的Model中,接着使用Update()方法将这个记录的字段更新为 200。 - 在后二行代码中,使用
Updates()方法批量更新多个字段,根据参数类型的不同分为以下两种方式。
- db.Model(&product).Updates(Product{Price: 200, Code: "F42"}),给定一个参数类型为
Product的结构体,表示只更新结构体中指定的字段(Price和Code),其它字段值不变。此方式仅会更新结构体中值为非零值的字段,零值字段跳过不更新。 db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}),给定一个 map 参数类型,表示根据key-value对来更新字段值,此方式会更新 map 中指定的所有字段值。
db.Delete(&product, 1)
删除数据
db.Delete(&product, 1)表示从数据库中删除product表中主键为1的记录,&product是指向需要删除的记录的指针。如果记录已经被删除或不存在该记录,则不执行任何操作并且返回零行受到影响。执行成功后将返回受影响的行数。