文章目录
- 1.ORM 是什么
- 2.GORM 是什么
- 3.安装
- 4.连接 DB
- 5.创建数据表
- 6.增加(Create)
- 7.查询(Read)
- 8.更新(Update)
- 9.删除(Delete)
- 10.小结
- 参考文献
1.ORM 是什么
ORM(Object Relational Mapping),中文名为对象关系映射。
使用 ORM 组件,可以让开发者通过操作对象的方式完成对数据库的操作(读写),避免手动书写 SQL 和完成数据到对象的转换,让我们更方便的操作数据库。
理论上 ORM 可以让我们脱离 SQL,但实际上还是需要懂 SQL 才能更好地使用 ORM。
2.GORM 是什么
GORM 是一个流行的 Golang ORM 库。
类似于 Java 生态里大家听到过的 Mybatis、Hibernate、SpringData 等。
GORM 由国人开发,中文文档齐全,对开发者友好,支持主流关系型数据库。
- MySQL
- SQL Server
- PostgreSQL
- SQlite
GORM 功能丰富齐全:
- 关联 (拥有一个,拥有多个,属于,多对多,多态,单表继承)
- 钩子(before/after create/save/update/delete/find)
- 支持 Preload、Joins 的预加载
- 事务,嵌套事务,保存点,回滚到保存点
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,使用 Map Find/Create,使用 SQL 表达式、Context Valuer 进行 CRUD
- SQL 构建器,Upsert,锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- 自动迁移
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
- 每个特性都经过了测试的重重考验
- 开发者友好
GORM 最新源码地址:go-gorm/gorm。
GORM V1 版本地址:jinzhu/gorm。
GORM 中文文档地址:这里。
本文将对 GORM 中常用的功能进行讲解,帮助你快速上手。
当然除了 GORM,你还有其他选择,比如 facebook-ent、sqlx 和 sqlc 等。
3.安装
基于 Go Module 开发,import 最新包然后 go get 即可。
go get -u gorm.io/gorm
// 不同 DB 对应的驱动
go get -u gorm.io/driver/sqlite
go get -u gorm.io/driver/mysql
go get -u gorm.io/driver/postgres
go get -u gorm.io/driver/sqlserver
驱动包按照自己实际使用的 DB 选择即可。
本文将以 MySQL 为例,讲解 GORM 的使用。
4.连接 DB
以 MySQL 为例,建立数据库连接。
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// MySQLConn GORM MySQL 连接。
var MySQLConn *gorm.DB
// Init gorm mysql connnection.
// 依赖服务配置初始化完成。
func InitMySQLConn() error {
// data source name.
dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", Conf.Mysql.User, Conf.Mysql.Passwd, Conf.Mysql.IP, Conf.Mysql.Port, Conf.Mysql.Dbname)
var err error
MySQLConn, err = gorm.Open(mysql.Open(dsn))
return err
}
填入 DB 对应的正确的用户名、密码、地址、端口、数据库名称等信息后,便可建立对应数据源的连接。相关配置一般在服务启动时,事先从配置文件中加载。
5.创建数据表
在进行增查改删(CRUD)之前,需要先创建一个数据表。
GORM 中一个 struct 对应一张数据库表,对应的 struct 被称为模型。
假如我们要创建一张商品(goods)表,那么 struct 可定义为:
// Good 商品。
type Good struct {
gorm.Model
Name string `gorm:"type:varchar(255) not null"`
Price int `gorm:"type:bigint not null"`
}
其中 gorm.Model 时 GORM 预先定义的一些基础字段,我们可以嵌入直接拿来用。
// Model a basic GoLang struct which includes the following fields: ID, CreatedAt, UpdatedAt, DeletedAt
// It may be embedded into your model or you may build your own model without it
//
// type User struct {
// gorm.Model
// }
type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt DeletedAt `gorm:"index"`
}
字段后的 tag 用来定义字段在 DB 中的相关属性,如 primarykey 表示主键,index 表示索引,type 表示字段类型。
除此以外,还有更加丰富的标签定义参见官方文档:字段标签。
一般在服务启动时创建数据表,如建立 DB 连接后只执行一次来完成数据表的创建。
db.AutoMigrate(&User{})
db.AutoMigrate(&User{}, &Product{}, &Order{})
// 创建表时添加后缀
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
比如创建我们上面的商品表。
// 自动创建表,如果表已经存在不会有任何动作。
err := MySQLConn.AutoMigrate(&Good{})
创建好后的数据表名为 struct 名称的 snake_case 复数形式,字段名为 struct 字段的 sanke_case 形式。
创建好的表结构如下:
6.增加(Create)
// createGood 插入商品。
func createGood(name string, price int) {
task := &Good{
Name: name,
Price: price,
}
result := MySQLConn.Create(task)
if result.Error != nil {
return
}
return nil
}
主键 ID 会自增,此外 GORM 还会自动维护 created_at、updated_ad 和 deleted_at 三个字段。
7.查询(Read)
比如按照主键查询。
// getGoodByID 根据主键检索。
func getGoodByID (id int) (Good, error) {
var good Good
result := MySQLConn.First(&good, id)
return good, result.Error
}
如果查不到,将报 “not found error” 错误。
再如按照其他字段进行 and 查询。
// getGoods 根据商品信息分页拉取。
func getTaskTypesByInfo(name string, price int, last_id uint) ([]Good, error) {
db := internal.MySQLConn
if name != "" {
db = db.Where("name = ?", req.TypeInfo.Name)
}
db = db.Where("price >= ?", price)
// 按照每页大小 50 拉取商品。
db.Where("id > ?", last_id).Order("id asc").Limit(50)
var goods []Good
result := db.Find(&goods)
return goods, result.Error
}
还有很多查询方式,比如按照 struct、map 指定查询字段以及 or 和 not 条件等,具体请参考官方文档 GORM 查询。
8.更新(Update)
比如更新所有字段。
使用 Save 方法更新所有字段,即使是零值也会更新。
// 先根据 ID 查询。
db.First(&good, 1)
// 再修改值。
good.Name = "小米"
// 最后写回。
db.Save(&user)
再如更改单列。
// 条件更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
再如更改多列。
Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段。
// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
9.删除(Delete)
如删除一条记录。
删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete,例如:
// 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";
再如根据主键删除。
GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,它可以使用数字,也可以使用字符串。
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);
注意:
如果您的模型包含了一个 gorm.DeletedAt 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!
如果您不想引入 gorm.Model,您也可以这样启用软删除特性:
type User struct {
ID int
Deleted gorm.DeletedAt
Name string
}
拥有软删除能力的模型调用 Delete 时,记录不会被数据库。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过普通的查询方法找到该记录。
使用 Unscoped 方法查找被软删除的数据。
db.Unscoped().Where("user_name = gry").Find(&users)
要想物理删除,使用 Unscoped 方法永久删除数据。
user.ID = 14
db.Unscoped().Delete(&user)
10.小结
本文简单介绍了 ORM、GORM、以及 GORM 连接数据库,创建数据表和 CRUD 的简单操作,帮忙新手快速上手。
更多用法,请参见官方文档 GORM 指南,这里有你想要的一切。
参考文献
GORM 指南| GORM - GORM
GORM 极速入门- 卢振千的博客
19-Gorm入门到精通- 刘清政 - 博客园
Go组件学习——gorm四步带你搞定DB增删改查 - 掘金