概述
- 什么是ORM:Object Relational Mapping 对象关系映射
- Gorm是一个已经迭代了10+年的功能强大的ORM框架。
- Gorm是go的一个数据库连接及交互框架,主要是把struct类型和数据库记录进行映射,数据库语句复杂的情况下可以直接手写语句,一般用于连接关系型数据库。
- 对应关系:数据表<-->结构体;数据行<-->结构体实例;字段<-->结构体字段
- 优点:提高开发效率
- 缺点:牺牲执行性能;牺牲灵活性,弱化SQL能力
- GORM 官方支持的数据库类型有:MySQL、PostgreSQL、SQLite、SQL Server 和 TiDB。GROM通过驱动来连接数据库,如果需要连接其它类型的数据库,可以复用或自行开发驱动。文中使用MySQL数据库做示例。
1. 安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql //这里安装的是MySQL驱动,换用其它数据库修改最后一个字段即可
2. 连接数据库
链接不同的数据库 都需要导入相对应数据库的驱动程序,GROM已经包装了一些驱动程序,直接按需要导入驱动即可。
连接MySQL
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
什么是dsn:Data Source Name,数据源名称。(详细可以打开上方代码中注释的链接查看)
注意:想要正确的处理 time.Time,需要带上parseTime 参数。要支持完整的 UTF-8 编码,需要将 charset=utf8更改为charset=utf8mb4。
MySQL 驱动程序提供了一些高级配置可以在初始化过程中使用,例如:
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})
3. 创建数据表
创建表之前,我们需要定义一个gorm.Model结构体使之与数据表对应。
这里用UserInfo做示例
type UserInfo struct {
ID uint
Name string
Gender string
Hobby string
}
创建表-自动迁移:使用db.AutoMigrate(&结构体名{})(自动迁移就是让新增的结构体字段自动增加到数据表中)
4. 增删查改
4.1. 创建(增加)
4.1.1. 创建记录(数据行)
使用db.Create()
创建一条数据:
运行后可以发现db1数据库中出现了表user_infos,表里也有一条我们创建的数据
同样可以用Create创建多条数据:
4.1.2. Upsert和冲突
使用clause.OnConflict处理数据冲突
4.1.3. 默认值
通过使用defalut标签为字段定义默认值
4.2. 删除
4.2.1. 物理删除
删除一条记录时,删除对象需要指定主键,否则会触发批量删除,删除所有匹配的记录。
db.Delete(&UserInfo{}, 1)
//删除一条id为1的记录 DELETE FROM user_infos WHERE id = 1;
db.Where("Name LIKE ?", "%小红%").Delete(&UserInfo{})
//批量删除名字为“小红”的记录 DELETE from emails where email LIKE "%小红%";
删除操作前:
删除一条id为1的记录后:
批量删除名字为“小红”的记录后:
4.2.2. 软删除
GORM提供了gorm.DeletedAt用于帮助用户实现软删除。拥有软删除能力的Model调用Delete时,记录不会被从数据库中真正删除。但GROM会将DeleteAt置为当前时间,并且不能通过正常的查询方法找到该记录。
使用Unscoped可以查询到被软删的数据。
4.3. 查询
GORM提供了First、Take、Last方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1条件,且没有找到记录时,它会返回ErrRecordNotFound错误
运行结果:
选择特定字段查询:Select允许指定要从数据库中检索的字段,否则将默认选择所有字段。
tips:
- 如果想避免
ErrRecordNotFound错误,可以使用Find,比如db.Limit(1).Find(&user),Find方法可以接受struct和slice的数据。对单个对象使用Find而不带limit,db.Find(&user)将会查询整个表并且只返回第一个对象,这是性能不高并且不确定的。 - 使用结构体作为条件查询时,GORM只会查询非零值字段。如果字段值为0、''、false或其他零值,该字段将不会被用于构建查询条件。要在查询条件中包含零值可以使用Map来构建查询条件。
4.4. 修改(更新)
Save会保存所有的字段,即使字段是零值。Save是一个组合函数。如果保存值不包含主键,它将执行Create,否则它将执行包含所有字段的Update。但是Save会更新 struct中全部的字段,未赋值的字段使用零值来更新,这样极易覆盖数据库中原有的值。- 使用Struct更新:
使用示例:db.Model(&user).Updates(UserInfo{...}),只会更新非零值,如果需要更新零值可以使用Map更新或使用Select选择字段。 - 使用Map更新:
使用示例:db.Model(&user).Updates(map[string]interface{}{...}) - 如果想更新指定字段,可以使用
Select、Omit
使用Map更新选定字段 举例:
// Select with Map
// 要修改的用户ID为`1`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "zhangsan", "hobby": "篮球"})
// UPDATE user_infos SET name='张三',hobby='篮球' WHERE id=1;
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "zhangsan", "hobby": "篮球"})
// UPDATE user_infos SET name='张三',hobby='篮球' WHERE id=1;
5. 扩展
GORM 事务
Grom 提供了Begin、Commit、Rollback方法用于事务;同时也提供了Transaction 方法用于自动提交事务,避免用户漏写Commit、Rollback。
Hook
Grom提供了CURD的Hook能力。Hook是在创建、查询、更新、删除等操作之前、之后自动调用的函数。如果任何Hook返回错误,Grom将停止后续的操作并回滚事务
性能提高
对于写操作(创建、更新、删除),为了确保数据的完整性,GROM会将它们封装在事务内部运行,但这会降低性能,可以使用SkipDefaultTransaction关闭默认事务。使用PrepareStmt缓存预编译语句可以提高后续调用的速度。
Grom的生态
Grom拥有丰富的扩展生态,常用扩展有:代码生成工具、分片库方案、手动索引、乐观锁、读写分离等。
如有错误,欢迎指正!
文中只简单叙述了GORM的基础用法,关于更多的GORM用法和细节可以查看官方文档(gorm.io/zh_CN/)