Go 数据存储篇(七):GORM 使用入门
1、ORM 与 GORM
我们已经成功存储数据到数据表,但是所有操作都要自行编写代码,很多编程语言和框架会引入 ORM 来解决模型类与数据表记录的映射关系,ORM 架起了 SQL 语句和应用程序之间的桥梁,将模型类和数据表映射起来,将模型类字段和数据表字段建立关联。
典型的 ORM 库比如 Java 中的 Hibernate、Ruby 中的 ActiveRecord、以及 Laravel 中的 Eloquent。
在 Go 语言中,也有这样的 ORM 库,最流行的当属 GORM。
GORM 是一个适用于 Go 语言的 ORM 库,遵循 ActiveRecord 模式进行设计。
注:ORM 有两种实现方式 —— ActiveRecord 和 DataMapper,关于两者之间的区别可以参考这篇教程:https://xueyuanjun.com/post/966.html。
GORM 的功能非常强大,除了基本的基于模型类对数据表进行增删改查之外,还支持定义关联关系、执行数据表迁移、查询链以及很多其他高级特性,并且支持在特定事件发生时(比如插入、更新、删除)触发指定的回调函数(类似 Laravel 框架的模型事件)。
下面我们来简单演示下如何基于 GORM 进行增删改查和关联查询。
2、GORM 使用示例
使用之前需要先安装 GORM:
go get github.com/jinzhu/gorm
然后我们编写一段示例代码:
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"time"
)
type Post struct {
Id int
Title string
Content string
Author string `sql:"not null"`
CreatedAt time.Time
Comments []Comment
}
type Comment struct {
Id int
Content string
Author string `sql:"not null"`
PostId int `sql:"index"`
CreatedAt time.Time
}
var DbConn *gorm.DB
func init() {
var err error
DbConn, err = gorm.Open("mysql", "root:root@/test_db?charset=utf8mb4&parseTime=true")
if err != nil {
panic(err)
}
DbConn.AutoMigrate(&Post{}, &Comment{})
}
func main() {
post := Post{Title: "GORM 示例教程", Content: "基于 GORM 进行数据库增删改查", Author: "学院君"}
// 通过 GORM 插入文章记录
DbConn.Create(&post)
fmt.Println(post)
// 通过关联关系新增评论并将其附加到对应的文章记录
comment := Comment{Content: "Test Comment", Author: "学院君小号"}
DbConn.Model(&post).Association("Comments").Append(comment)
// 查询文章记录
var gormPost Post
DbConn.Where("author = ?", "学院君").First(&gormPost)
// 查询包含评论数据的文章记录
var comments []Comment
DbConn.Model(&gormPost).Related(&comments)
fmt.Println(comments[0])
}
由于 GORM 会根据模型类结构体声明自动创建对应的数据表,所以我们可以删除 test_db
数据库中的 posts
和 comments
表,然后运行这段代码看看结果是否符合预期:
可以看到,数据表的插入和关联查询结果都是正常的。下面我们简单分析下这段示例代码。
3、GORM 运行原理
数据库连接
由于 GORM 也实现了 database/sql
接口,所以建立数据库连接和之前使用 go-sql-driver/mysql
包类似,只是调用方法改成了 gorm.Open
:
DbConn, err = gorm.Open("mysql", "root:root@/test_db?charset=utf8mb4&parseTime=true")
参数和之前完全一样,引入的驱动包调整为 jinzhu/gorm
即可:
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
注:更多关于 GORM 数据库连接配置的细节,请参考 GORM 官方文档(https://gorm.io/zh_CN/docs/)。
数据表自动迁移
和使用 go-sql-driver/mysql
包不同的是,这次我们不再需要手动创建数据表,因为 GORM 提供了数据表自动迁移功能:
DbConn.AutoMigrate(&Post{}, &Comment{})
通过 AutoMigrate
方法传入要迁移的模型类实例即可,GORM 会自动创建对应的数据表,表名规则是模型类名小写的复数形式。
模型类定义
接下来,我们看下模型类的定义:
type Post struct {
Id int
Title string
Content string
Author string `sql:"not null"`
CreatedAt time.Time
Comments []Comment
}
type Comment struct {
Id int
Content string
Author string `sql:"not null"`
PostId int `sql:"index"`
CreatedAt time.Time
}
这里定义了两个模型类,Post
和 Comment
,分别对应数据表 posts
和 comments
,并且在 Post
中通过如下方式定义了 Post
和 Comment
之间的一对多关联:
Comments []Comment
这里我们没有用结构体标签指定关联外键(GORM 支持通过结构体标签设置数据表字段属性),GORM 底层会自动维护这个关联,默认规则是在 Comment
中的 PostId
字段(即当前模型类名加上主键 ID 后缀)。
但是还是有一些字段设置了结构体标签,这是为了给该字段添加额外的数据表字段约束,比如索引、是否允许为空等:
Author string `sql:"not null"`
PostId int `sql:"index"`
注:更多模型类定义的细节和结构体标签设置,请参考 GORM 官方文档。
增删改查
我们继续来看增删改查和关联模型的操作,在 GORM 中,我们总算不用维护 SQL 语句了,所有的增删改查操作都可以通过 GORM 库提供的方法来实现,比如要创建一条记录可以这么做:
post := Post{Title: "GORM 示例教程", Content: "基于 GORM 进行数据库增删改查", Author: "学院君"}
DbConn.Create(&post)
模型类中的 Id
和 CreatedAt
字段系统会自动维护,不需要显示设置。
注:如果要实现修改、删除和查询操作,请参考 GORM 官方文档 CRUD 接口部分,这些也都有相应的内置方法。
关联查询
如果要在上述模型实例上创建与之关联的评论,可以这么做:
comment := Comment{Content: "Test Comment", Author: "学院君小号"}
DbConn.Model(&post).Association("Comments").Append(comment)
最后要查询包含关联评论记录的主题,可以这么做:
var gormPost Post
DbConn.Where("author = ?", "学院君").First(&gormPost)
var comments []Comment
DbConn.Model(&gormPost).Related(&comments)
注:更多关联查询和操作的使用,请参考 GORM 官方文档的关联部分。
4、小结
可以看到,引入 GROM 之后,我们不再需要手动编写复杂的 SQL 语句,只需要借助 GORM 提供的方法就可以非常便捷地完成数据库交互,这极大简化了我们日常开发的代码编写和维护成本,也降低了安全风险,所有的 SQL 语句都由 GORM 底层去构建并执行,它会将上层模型实例的增删改查、关联操作方法执行转化为相应的 SQL 语句去执行,这也是 ORM 的设计初衷。
以上只是 GORM 的简单示例,完整功能请阅读官方文档(https://gorm.io/zh_CN/docs/),需要指出的是 GORM 的作者是中国人,所以中文文档支持非常友好。
关于 Go 语言的数据库操作部分,我们就简单介绍到这里,到目前为止,还并没有看到 Go 语言相对于 PHP 开发 Web 应用的任何优势,接下来,我们来看看 Go 语言 Web 编程的重头戏 —— 并发编程,这也是 Go 语言本身的优势所在。
(全文完)
推荐阅读