数据库
数据是现代应用程序最宝贵的财产,而数据库(database)是管理、持久化保存数据的不可或缺的组件。数据库是一个存放数据的仓库,这个仓库是按照一定的数据结构(数据结构是指数据的组织形式或数据之间的联系)来组织、存储的,我们可以通过数据库提供的多种方法来管理数据库里的数据。数据库主要有关系型和非关系型数据库。
对数据库编程是几乎任何编程语言都有提供的基础功能模块,无论是编程语言内置的支持,还是通过外部库来实现,Golang也不例外;当然啦,不同编程语言提供的数据库编程API是不尽相同的,而且需要支持的数据库也是多种多样,如常用的 MySQL
,SQLServer
,Postgres
等数据库。
在编程中我们常做的操作是 CRUD
:
C
是Create
,代表新建或向数据库插入新记录R
是Read
, 从数据库中检索记录U
是Update
,改变数据库中记录的内容D
是Delete
,从数据库中删除记录。
Golang 连接数据库
Golang 提供了多个能实现 CRUD 操作的库,有内置的 low-level 的标准库 database/sql
,还有功能丰富的 high-level 的第三方库 GORM
,sqlx
,sqlc
等。
database/sql
可以通过官方文档查看使用方法,它运行速度快,代码编写起来简单,但缺点是必须手动将SQL
字段映射到变量,非常容易出错,如果变量的顺序不匹配,或者忘记将一些参数传递给函数调用,错误就只会在运行时出现。
sqlx
是对标准库 database/sql
的扩展,速度几乎和标准库一样快,使用也非常简单,字段映射是通过查询文本或结构标签完成的,但同样错误只会在运行时捕获。在写作本文时其原 Github 仓库已有一年多没有更新了,所以暂时放弃它。
sqlc
是编写 SQL
查询语句,自动生成对应的 Golang
代码,它有任何错误都会立即捕获,而不需要等到运行时才知道,目前支持 PostgreSQL
, MySQL
, SQLite
。
GORM
是一个非常完善的ORM框架,支持很多主流数据库,对开发者非常友好,使用广泛,
GORM
接下来介绍下 GORM 的简单使用方法,为了方便,选用 sqlite3 作为数据库。
package main
import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type UserInfo struct {
Id int `gorm:"primaryKey"`
Name string
Gender string
Hobby string
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// 数据库迁移,如果不存在则新建表,存在判断字段是否一致不一致修改。
db.AutoMigrate(UserInfo{})
// Create
db.Create(&UserInfo{
Name: "Simon",
Gender: "male",
Hobby: "Fitness",
})
// Read
var u UserInfo
// 用来存放结果的结构体,第二个参数,id
db.First(&u, 1)
fmt.Println(u.Name)
// Update
db.First(&u, 1).Update("hobby", "running")
fmt.Printf("姓名:%s, 爱好:%s\n", u.Name, u.Hobby)
db.Model(&u).Update("Name", "Tom")
fmt.Printf("姓名:%s, 爱好:%s\n", u.Name, u.Hobby)
// 更新多个字段
db.Model(&u).Updates(UserInfo{Name: "Alice", Hobby: "Swimming"}) // 仅更新非零值字段
fmt.Printf("姓名:%s, 爱好:%s\n", u.Name, u.Hobby)
// Delete - 删除 product
db.Delete(&u, 1)
}
例子通过迁移的语句 db.AutoMigrate
来创建表。查询(db.First
),更新(Update
),删除(Delete
)中的参数均为结构体的地址。
GORM 进阶
GORM作为ORM框架并没有提供任何辅助代码开发的功能,导致面对较为复杂的数据库表查询场景时,开发者需逐条手写数据表中的列与对应结构体的成员变量,单调且重复的查询功能也需要手动复制,稍不注意就会造成不易察觉的拼写错误。
其实在 Golang 泛型比较弱的情况下,使用【代码生成】依然是解决个性化场景的经典方案,这样绕开了 interface{},我们就可以做更多校验,也省去了断言。GORM 其实也是基于这个思路,推出了自己的【代码生成工具】:Gen。
GORM Gen 可以连接数据库,根据指定的表生成相应的 Golang 模型代码,非常方便,具体操作留待后篇。