ORM(Object Relational Mapping),中文名为对象关系映射。
使用 ORM 组件,可以让开发者通过操作对象的方式完成对数据库的操作(读写),避免手动书写 SQL 和完成数据到对象的转换,让我们更方便的操作数据库。
理论上 ORM 可以让我们脱离 SQL,但实际上还是需要懂 SQL 才能更好地使用 ORM。
GORM 是一个流行的 Golang ORM 库。
类似于 Java 生态里大家听到过的 Mybatis、Hibernate、SpringData 等。
GORM 由国人开发,中文文档齐全,对开发者友好,支持主流关系型数据库。
GORM 功能丰富齐全:
GORM 最新源码地址:go-gorm/gorm。
GORM V1 版本地址:jinzhu/gorm。
GORM 中文文档地址:这里。
本文将对 GORM 中常用的功能进行讲解,帮助你快速上手。
当然除了 GORM,你还有其他选择,比如 facebook-ent、sqlx 和 sqlc 等。
基于 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 的使用。
以 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 对应的正确的用户名、密码、地址、端口、数据库名称等信息后,便可建立对应数据源的连接。相关配置一般在服务启动时,事先从配置文件中加载。
在进行增查改删(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 形式。
创建好的表结构如下:
// 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 三个字段。
比如按照主键查询。
// 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 查询。
比如更新所有字段。
使用 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;
如删除一条记录。
删除一条记录时,删除对象需要指定主键,否则会触发 批量 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)
本文简单介绍了 ORM、GORM、以及 GORM 连接数据库,创建数据表和 CRUD 的简单操作,帮忙新手快速上手。
更多用法,请参见官方文档 GORM 指南,这里有你想要的一切。
GORM 指南| GORM - GORM GORM 极速入门- 卢振千的博客 19-Gorm入门到精通- 刘清政 - 博客园 Go组件学习——gorm四步带你搞定DB增删改查 - 掘金